This is the seventh day of my participation in the August More text Challenge. For details, see: August More Text Challenge

1. Introduction

In the Java language, Throwable is the parent class of all exceptions, just as Object is the parent class of all objects. Why are there exception classes? Programs are developed by people, and people will inevitably make mistakes, so the program may run exceptions. Once an exception occurs, the first thing the developer needs to do is locate the exception and then resolve it.

It’s up to the developer to fix the exception, but it’s up to the Java language to quickly locate the exception.

Therefore, the exception’s base Throwable class has a very important property called stackTrace, which represents the stack information that was running on the current thread at the time of the exception. It allows you to quickly locate which line of code the exception was thrown in which method of which class.

// Exception details
private String detailMessage;

// Stack list
private StackTraceElement[] stackTrace;
Copy the code

The detailMessage is manually specified by the developer, while the stackTrace stack is automatically captured by the JVM.

The following example program, unconditional throw exception.

public class Demo {

	public static void main(String[] args) {

	static void throwException(a) {
		throw new RuntimeException("Throw an exception..."); }}Copy the code

The console output is as follows:

The Exception in the thread "main" Java. Lang. RuntimeException: throw exceptions... at top.javap.exception.Demo.throwException( at top.javap.exception.Demo.main( the code

Through the console output of the exception information can quickly locate the exception, very convenient. At this point, you must wonder how good the STACK information captured by the JVM is for locating exceptions at a glance.

Easy to use is easy to use, but there is a price behind easy to use. This kind of exception creation is very expensive, and each exception object is created, the JVM needs to grab the current thread running stack information.

2. Performance comparison

Exceptions are great, but don’t abuse them. Let’s use an example to get a feel for how slow it is to use exceptions to process business logic.

[Requirement] Given a string S, determine whether S is a number.

[Implement A- non-exception]

public static boolean isNumber(String s){
	for (char c : s.toCharArray()) {
		// Compare each character to be a number
		if(! Character.isDigit(c)) {return false; }}return true;
Copy the code

【 Implement B-exception 】

public static boolean isNumber(String s) {
	try {
		// An attempt is made to force an Integer, and a NumberFormatException is thrown if the conversion fails
	} catch (Exception e) {
		return false;
	return true;
Copy the code

Executed ten million times, the test results are as follows:

The string S Solution A Time Consuming (ms) Plan B Time (ms)
123 154 19
123a 161 9809

As you can see, when the string S is a normal number, plan B does not throw an exception, and the performance is about the same, or even a little better. Once the string S is not a number, Plan B starts throwing exceptions and the performance plummets, nearly 60 times slower than the non-exception approach!!

Now, this is pretty straightforward, but I think it speaks for itself.

3. Optimize abnormal efficiency

Exceptions are easy to use. In business processing, if the operation does not meet the requirements, it is very convenient to throw an exception and end the process. But over time, the whole system is prone to extreme abuse.

How do you have your cake and eat it? I want the convenience of exceptions without compromising performance. Is there a good way? Of course there is, which is to reduce the cost of exception creation.

The cost of creating an exception object is high mainly because it calls the fillInStackTrace() method in the constructor to capture the stack information, which is very expensive.

public synchronized Throwable fillInStackTrace(a) {
    if(stackTrace ! =null|| backtrace ! =null
        stackTrace = UNASSIGNED_STACK;
    return this;
Copy the code

As long as you skip this step, creating an exception object is just like creating a normal object.

The fillInStackTrace() method is not final, which means that subclasses can override it, so we just need to create a lightweight business exception class and override it to implement an efficient exception class.

public class LightBizException extends BizException {

	public LightBizException(String message) {

	public synchronized Throwable fillInStackTrace(a) {
		// Override to prohibit fetching stack information
		return this; }}Copy the code

Alternatively, you can set writableStackTrace to false in the constructor so that the stack information is not captured.

/ * * *@paramMessage Exception details *@paramCause Which exception causes the current exception *@paramEnableSuppression Specifies whether to enable exception suppression@paramWritableStackTrace Whether to enable stack tracing */
protected Throwable(String message, Throwable cause,
                    boolean enableSuppression,
                    boolean writableStackTrace) {
    if (writableStackTrace) {
    } else {
        stackTrace = null;
    detailMessage = message;
    this.cause = cause;
    if(! enableSuppression) suppressedExceptions =null;
Copy the code

Both of the above methods can achieve efficient exception classes, as long as the stack information is not captured, the cost of creating exception classes will be greatly reduced. In this way, you can easily use exceptions to do process control without worrying about performance issues.

4. Think about

The downside of a lightweight exception class that doesn’t grab stack information is that you can’t trace it anymore. Without the stack, you have a hard time locating where exceptions are being generated. But looking back, if you can’t trace it, you can’t trace it. Do you really need all the exception stacks?

When processing business logic, most of the time to do business verification, just to filter illegal requests, throw an exception, refuse to execute the subsequent business logic, the purpose of exception is only to do process control. For example, a method to modify user information, the most basic is to verify that the user ID can not be empty, this exception is known, do you think this stack of exceptions still make sense?

5. To summarize

Exceptions are easy to use, but exception objects are expensive to create. By default, the stack is fetched every time. This is one of the main reasons for the high cost of creating exceptions.

The downside is that there is no stack to locate exceptions. However, we do not need the stack information for all exceptions. For known exceptions, such as those thrown by parameter verification, there is no need to record the stack. In this case, we can optimize the efficiency of exceptions.

However, it is necessary to keep the stack for unforeseen and unknown system exceptions!!