Hello, THIS is Yes.

This series of questions are illustrated, to understand the main, I can extend as far as possible to extend ~

The answer is my original hand, if wrong, welcome to correct ~

After watching the warriors, please leave a message to let me know your cowhid.

Without further ado, GOGOGO!

1. What do you think is great about Java?

This kind of general problem if did not have thorough, systematic understanding to certain knowledge point absolutely meeting meng!

So why do you often see interviewers asking you big, empty questions? It’s a test of whether you have a systematic understanding.

Back to the question. I think it can be explained from three aspects: cross-platform, garbage collection and ecology.

First of all, Java is cross-platform, and the machine code executed by different platforms is different. Because Java has an intermediate LAYER JVM, it can Write multiple platforms at a time, that is, “Write once,Run anywhere”.

Compilation execution process is the Java source code is first compiled into bytecode, bytecode is interpreted by JVM or JIT Compilation execution, and because JIT Compilation needs to be preheated, so it also provides AOT (Ahead-of-time Compilation), can directly convert bytecode into machine code. To reactivate the program quickly.

(Interpretation execution is less efficient than compilation execution. Do you think it is faster to give you English to translate and read or to give you Chinese to read directly?)

Java also provides automatic garbage collection, and while managing memory manually means free, fine-grained control, it is error-prone.

In the current abundant memory, the memory management to GC to do, reduce the programmer programming burden, improve the development efficiency, more cost-effective!

And now the Java ecosystem is full, with rich third-party libraries, comprehensive materials on the web, enterprise-level frameworks, middleware of all kinds, whatever you want.

Basically this answer is about the same, and then waiting for the interviewer to extend.

Of course, there is no fixed answer to this open-ended question, so my answer is for reference only.

2. How would you design a HashMap?

I think we can start with some key points of HashMap, such as hash functions, how to handle conflicts, and how to expand.

Let’s start with your understanding of HashMap.

For example, a HashMap is nothing more than a collection of <key,value> formats that can be used to quickly find a value using a key.

The basic principle is to hash the key through the hash function to get the hash value, and then find the corresponding index by modulo the array with the hash value.

So the hash function is critical not only to be fast, but also to be evenly distributed and to minimize hash collisions.

Since the input value is infinite and the array size is finite, there are bound to be collisions, so you can use the zipper method to handle collisions.

To avoid malicious hash attacks, the zipper can be converted to a red-black tree structure when it exceeds a certain length.

Of course, beyond a certain number of nodes still need to expand, otherwise the collision will be too serious.

However, common capacity expansion will lead to a long put delay, especially when HashMap stores a large amount of data. Therefore, it can be considered to delay the movement of two tables like Redis, and only one table can be moved at a time.

However, this memory is relatively tight, so also depends on the scene to trade off.

However, it is best to use pre-estimated data size to avoid frequent expansion.

Basically, that’s about it. All the key elements of a HashMap are included.

It may extend to issues like thread safety, as currentHashMap does anyway.

3. What are the thread pool implementations provided by the concurrent library?

Although the Alibaba Java development manual forbids the use of these implementations to create thread pools, I’ve been asked this question several times and it’s a hot topic.

This leads to questions about how thread pools are designed.

Let me talk about the internal logic of the thread pool first, so you can understand these implementations.

First, the thread pool has several key configurations: core thread count, maximum thread count, idle lifetime, work queue, and rejection policy.

  1. By default, threads are not pre-created, so they are created after the task (setting prestartAllCoreThreads precreates core threads).
  2. Instead of creating new threads when the core thread is full, tasks are stacked on the work queue.
  3. If the work queue runs out, new threads are added until the maximum number of threads is reached.
  4. If the work queue is full and the maximum number of threads has been reached, the incoming task will execute the reject policy.
  5. If a thread is idle longer than the idle lifetime and the number of threads is greater than the number of core threads, the thread is destroyed until the number of threads is equal to the number of core threads (allowCoreThreadTimeOut can reclaim core threads).

Back to the interview questions, the implementation refers to the 5 static factory methods for Executors:

  • newFixedThreadPool
  • newWorkStealingPool
  • newSingleThreadExecutor
  • newCachedThreadPool
  • newScheduledThreadPool

newFixedThreadPool

The thread pool implementation features the same number of core threads as the maximum number of threads, a keepAliveTime of 0, and an unbounded queue.

According to these Settings, it can be known that the number of task threads is Fixed, as its name is Fixed.

Then, OOM may occur, because the queue is unbounded, so the task may burst the memory.

Its feature is that I fixed out so many threads, redundant tasks on the queue, even if the queue is overflowing I also do not care.

It is therefore not recommended to create thread pools in this way.

newWorkStealingPool

ForkJoinPool this is the thread pool that we used in 1.8. This is the thread pool that ForkJoinPool returns.

Such as users. ParallelStream (). The filter (…). .sum(); ForkJoinPool is used.

As you can see from the figure, the number of threads is referenced to the number of processing cores currently available on the server. I remember the number of parallel cores is -1.

The property of this thread pool is known by its name, Stealing tasks.

Each thread has its own double-ended queue. When the tasks in its queue are finished, it takes the tasks from the end of the task queue of other threads to execute them, speeding up the execution rate of the tasks.

ForkJoin divides and conquer, breaking large tasks into smaller ones, allocating them, and then adding up the results

newSingleThreadExecutor

This thread pool is very unique, a thread pool is only one thread, one person, one city, equipped with an unbounded queue.

Its feature is to ensure that tasks are executed sequentially.

newCachedThreadPool

This thread pool is impatient, the number of core threads is 0, the maximum number of threads is considered infinite, and the task queue has no storage space, simply interpreted as a task must find a thread to continue, or it will block.

Cached means that it will cache any previously executed threads for 60 seconds, at which point if a task comes in it can be executed from the previous thread.

So it’s good for scenarios where you have a lot of short tasks in a short period of time. If no thread is available, a new thread will be created to execute the task and respond quickly to the task.

However, if the task is long, there are many threads, context switches are frequent, switching costs are obvious, and there are too many threads in memory, and there is a risk of OOM.

newScheduledThreadPool

It’s a timed task, and the point is that delayed queue.

Timer, DelayQueue, ScheduledThreadPool, etc. This article also introduces the application of time wheel in Netty and Kafka.

4. What if you were to design a thread pool?

This kind of design problem is the same, first say understanding, show that you know the use and principle of this thing, and then start BB. It’s basically taking an existing design and adding some personal insights.

A thread pool is a container for storing threads, which can be used to repeatedly execute tasks, reduce the overhead of creating and destroying threads, improve the response speed of tasks, and facilitate the management of threads.

I personally feel that if you want to design a thread pool, you have to consider the management of worker threads in the pool, task scheduling, thread pool overload, monitoring.

The number of initialization threads, the number of core threads, and the maximum thread pool are all exposed as configurable, including the thread-idle death configuration that exceeds the number of core threads.

The storage structure of tasks can be configured to be either unbounded or bounded queues. Tasks of different priorities can be allocated to multiple queues according to configuration, or thread utilization can be improved by using a mechanism called stealing.

Change the execution strategy of the task by providing a configuration indicating whether the thread pool is IO – or CPU – intensive.

There are many scenarios for overload, including dropping a task, rejecting a task and throwing an exception, discarding the oldest task, or customization, and so on.

Thread pool burying points expose interfaces for monitoring such information as number of processed tasks, number of pending tasks, number of running threads, number of rejected tasks, etc.

I think basically this answer is about right, waiting for the interviewer to ask questions.

Note that you don’t need to explain the number of core threads to the interviewer.

Of course, this kind of open question is still different, I am not the standard answer, only for reference.

5. How does GC tune?

GC tuning is definitely a case by case issue, but don’t go into too much detail in an interview. You don’t need to get into specific garbage collector parameters like CMS tuning, G1 tuning, etc.

The core idea of GC tuning is to recycle objects as much as possible in the young generation and reduce the number of objects in the old generation.

Tuning depends on the scenario based on GC logs. Common indicators to watch are the frequency of Young and Full GC triggers, causes, rate of promotion, memory usage of older generations, and so on.

For example, it is found that Full GC is generated frequently, and after analyzing the log, it is found that there are no memory leaks, but that a large number of objects are aged after the Young GC, which eventually triggers Ful GC. Therefore, we can know that the Survivor space is set too small, causing the object to enter the old age prematurely, so we adjust Survivor.

Or the promotion age may be set too low, or the log may be analyzed to discover a memory leak, or a third party library called System.gc, etc.

Anyway, specific scene specific analysis, the core idea is to try to recycle the object in the new generation.

Basically, just answer like this, and then wait for the interviewer to extend.

6. What is dynamic proxy?

Dynamic proxy is a proxy mechanism, dynamic as opposed to static.

A proxy can be thought of as a wrapper around the calling target, and is usually used to do some logic before calling the actual target, eliminating duplicate code.

Static proxy means that we pre-code a proxy class, while dynamic proxy means that the runtime generates the proxy class.

Dynamic is more convenient in that you can specify a set of targets to generate proxy classes (AOP) dynamically, rather than writing the corresponding proxy class for each target class as static proxies do.

Proxies are also a form of decoupling, between the target class and the caller, because there is an additional layer of proxy classes.

Common dynamic proxies are JDK dynamic proxies and CGLIB.

7.JDK dynamic proxy and CGLIB difference?

JDK dynamic proxies are interface-based, so the proxy class must have a defined interface.

CGLIB is based on the ASM bytecode generation tool, which implements proxy classes by inheritance, so be aware of final methods.

The performance varies with JDK versions. The following is taken from Haiq’s blog

  • Under JDK6, the difference between JDK dynamic proxy and Cglib is not obvious in the case of fewer runs, or even faster. Cglib performs slightly faster as the number of calls increases
  • In JDK7, the situation is reversed! With fewer runs (1,000,000), the JDK dynamic proxy was almost 30% faster than Cglib; As the number of calls increased (50,000,000), the dynamic proxy was nearly twice as fast as Cglib
  • Jdk8 performs basically the same as JDK7

JDK dynamic proxy implementation principle:

  1. Start by implementing the InvocationHandler interface to get an aspect class.
  2. Then use Proxy to get a Proxy class based on the class loader, interface, and aspect class of the target class.
  3. The logic of the proxy class is to forward all interface method calls to the invoke() method of the tangent class, and then invoke the target class’s methods based on reflection.

A little bit further down the line is that the proxy class will be present in the static block and will reflect all the methods into the static variable. I have seen the decompile proxy class before, and I wrote it up quickly, and it will look something like this:

This set of JDK dynamic proxy principle should be very clear.

CGLIB implements the same logic as JDK dynamic proxies, but in a different way.

Enhancer en = new Enhancer(); En.setsuperclass (target.getClass())); // set this to be the implementation class of the JDK dynamic proxy handler en.setcallback (this); //4. Create a proxy object that is a subclass of the target class. return en.create();Copy the code

It then implements the logic of the call through bytecode generation techniques rather than reflection, without further ado.

8. How do annotations work?

An annotation is simply a tag. You can tag a class, a method, a property, etc., and the tag itself can set some values.

Once we have a tag, we can get that tag at parsing time and do something special, and that’s where annotations come in.

For example, we can define aspects to see if there is an annotation mark on the method when executing it, and if so, we can perform some special logic (RUNTIME annotations).

There are three main categories of annotation lifecycle:

  • Retentionpolicy. SOURCE: used by the compiler, not written to the class file
  • Retentionpolicy. CLASS: this information is written to the CLASS file and discarded during CLASS loading, i.e., at runtime
  • Retentionpolicy. RUNTIME: this is written to a class file, which is stored permanently and can be reflected to retrieve annotation information

So I did not specify what parsing is when I wrote above, because parsing actions are different in different life cycles.

As usual:

Override Override is not included in the class file. Override is not included in the class file.

For example, Autowired, which is common in Spring, is RUNTIME, so the annotation information can be reflected at RUNTIME, as well as the value of the tag required.

So an annotation is a tag that can be used by the compiler or at runtime.

9. Have you ever used reflex?

If you have used it, I don’t need to say anything more, just say something about it, and then wait for the interviewer to continue digging.

If not, say not in production, but in private research on the principles of reflection.

Reflection is the ability provided by Java to obtain information about an object at runtime, including properties, methods, annotations, etc., and to call its methods.

Reflection is not used in general coding, but is used more in the framework, because many scenes need to be very flexible, so the type of the target object is not determined, and the object information can only be obtained dynamically through reflection.

PS: Do not understand reflection, you can check online, here is not in-depth.

10. Can you talk about the class loading process?

Class loading, as its name suggests, loads a class into the JVM, enters a binary stream into memory, parses it, processes it and turns it into a usable class, and that’s what class loading does.

The binary stream can come from a class file, bytecode generated by a bytecode tool, or from the network, and the JVM will accept any binary stream that fits the format.

The class loading process is divided into three stages: loading, connection and initialization. Connection can also be divided into three stages: verification, preparation and parsing.

So in general, it can be divided into five stages:

  • Load: Load the binary stream into memory, generating a Class.

  • Validation: Mainly to verify that the loaded binary stream conforms to a certain format, specification, current JVM version, and so on.

  • Preparation: Assigns initial values to static variables (class variables), i.e. allocates memory space for them in the method area. Notice that it’s a static variable and it’s an initial value, so int starts at 0.

  • Parse: Convert symbolic references from the constant pool to direct references. A symbolic reference can be interpreted as A substitute label, for example, if you’re going to do A project and no one is available, you set an A to do it. Then when the plan is really to land, we must find a certain person, and then xiao Ming will do something.

    Parsing is replacing A(symbolic reference) with Ming (direct reference). A symbolic reference is a literal that has no real meaning, just a representation. A direct reference is a real reference through which the target can be found in memory.

  • Initialization: this time to execute some static code blocks, static variable assignment, the assignment is the code inside the assignment, the preparation stage is just to set the initial value of the pit.

I think the answer to this question can be more coarse than I wrote, a few stages say, roughly do say ok.

For a more detailed overview of the process, see the virtual machine class loading section in Understanding Virtual Machine Java.

11. Do you know about parental delegation? What do you say?

The class loading mechanism basically asks the parent delegate.

Parental delegation means:

If a class loader needs to load a class, it first delegates the classloading request to the parent class loader, and then to the parent class if it still has a parent class, at each level.

It recurses all the way to the top, and when the parent loader cannot complete the request, the subclass tries to load it.

The mother is the parent of the child.

The parent class is not what we call an inheritance relationship, but the call logic is.

I’ve written about parental delegation before, and HERE are some of the more important points:

Java itself provides three types of loaders:

  1. Start the Bootstrap ClassLoader, which is a part of the virtual machine and implemented in C++. It is responsible for loading files in

    \lib or in the path specified by -xbootclasspath and whose names are recognized by the virtual machine. It is the father of all class loaders.

  2. The Extension ClassLoader, which is implemented in Java and independent of the virtual machine, is responsible for loading libraries in the

    \lib\ext directory or in the path specified by the java.ext.dirs system variable.

  3. Application ClassLoader, which is implemented in Java and is independent of the virtual machine. The main thing is to load the libraries on the user’s classPath, which is the default loader in our application if we don’t implement a custom class loader.

So in general class loading will delegate from the application class loader to the extension class and then to the launcher class, the launcher class can’t find it and then the extension class can’t find it and then the application class loader can’t find it.

The parent delegate model is not a mandatory constraint, that is, you can’t get anything wrong if you don’t do it, but a way that JAVA designers recommend using classloaders.

Why parent delegate?

It makes classes hierarchical. In the case of java.lang.Object, loading it through layers of delegation is eventually loaded by Bootstrap ClassLoader, The java.lang.Object in <JAVA_HOME>\lib is loaded by the Bootstrap ClassLoader into the JVM.

This way, if a rogue creates a java.lang.Object with bad code embedded in it, if we implement it in the parent delegate model, the only thing that ends up loading into the JVM is what’s in our Rt.jar, which is the core base class code protected.

This mechanism causes only one Java.lang.object to appear in the system. There will be no confusion. Imagine if we had two objects in the JVM.

Do you know of any violations of parental mandates?

A typical example is JDBC.

The JDBC interface is defined by the class library, but the implementation is in the JAR package provided by the major database vendors. The implementation class cannot be found by starting the class loader, so the application loader is required to complete the task, which violates the bottom-up delegation mechanism.

To do this, we use a thread-context classloader, using setContextClassLoader() to set the application classloader by default, And then through the Thread. Current. CurrentThread () getContextClassLoader () to obtain the class loader to load.

12. What is the difference between JDK and JRE?

The Java Runtime Environment (JRE) refers to the Java Runtime Environment, including the JVM and Java class libraries.

Java Development Kit (JDK) can be regarded as a superset of JRE. It also provides some tools such as various diagnostic tools: JStack, JMap, jstat, etc.

13. What JDK tools have you used?

This inspection on your daily inside have some tools for problem analysis, investigation.

If you have used it must be very easy to say, such as the previous troubleshooting of memory exceptions with Jmap dump down memory files with MAT analysis and so on.

If you haven’t tried it before, try it out on your own.

I’ve listed a few tools that I’ve written about in the past and suggested that I use them myself.

  • JPS: tool for vm process status
  • Jstat: monitors vm statistics
  • Jmap: Java memory mapping tool
  • Jhat: vm heap dump snapshot analysis tool
  • Jstack: Java stack tracing tool
  • Jinfo: Java configuration information tool
  • VisualVM: a graphical tool that gives you access to running information about a virtual machine: memory analysis, CPU analysis, etc. It is no longer packaged in JDK by default starting in JDK9.

There are many more tools, check out the screenshot below.

More details can be found in Chapter 4 of Understanding Virtual Machine Java.

In short, it is to find their own opportunities to use, no opportunity to create their own opportunities, prevent.

What is the difference between an interface and an abstract class?

Interface: contains only abstract methods, not member variables, when has a.

An interface is an abstraction of behavior, similar to a treaty. In Java, interfaces can be implemented more than once. From the perspective of has A, interfaces come first, that is, interfaces are agreed first, and then implemented.

Abstract classes: Can contain member variables and general methods and abstract methods. When using abstract class inheritance for code reuse scenarios, subclasses must implement abstract methods in the abstract class.

Only single inheritance is supported in Java. From the IS A point of view, you write first, then you find the code reusable, and then you abstract an abstract class.

15. What is serialization? What is deserialization?

Serialization is simply converting an object into a transportable byte sequence format for easy storage and transmission.

Because objects can be considered “solid” in the JVM, there are various references, such as memory address Ox1234 referring to something, which needs to be “squashed” before the object is transferred across the network.

Because the memory address Ox1234 at the other end of the network can have no such and such object, the transmitted object needs to contain this information, and then the receiver deserializes the flattened information to get the object.

So deserialization is the process of converting a byte sequence format into an object.

Let me extend Java serialization.

First, Serializable. This interface has no real meaning, just serves as a marker.

In addition to strings, arrays, and enumerations, writeOrdinaryObject is used if this interface is implemented, otherwise serialization is thrown incorrectly.

What does the serialVersionUID do?

private static final long serialVersionUID = 1L;

You’ll often see code that verifies that the serialized object has the same ID as the deserialized object.

Therefore, the number of this ID is not important, whether 1L or IDEA is automatically generated, as long as the serialVersionUID of the object is the same as the serialVersionUID of the object during serialization.

If the specified serialVersionUID is not displayed, the compiler automatically generates one, which can be considered a fingerprint, based on information about the class.

So if you do not define a serialVersionUID and then serialize an object, and change the structure of the object’s class before deserialization, such as adding a member variable, deserialization will fail.

Because the structure of the class changes, the generated fingerprint changes, so the serialVersionUID is inconsistent.

So the serialVersionUID serves as validation.

Java serialization does not include static variables

Simply put, serialized contents do not contain the values of static variables, as the following code makes clear.

16. What is immutable class?

Immutable classes mean that you can’t change the value of an object. A String is a typical immutable class. Once you create a String, it can’t be changed.

Because it cannot be modified, like s += “a”; This method returns a new String. The old s reference does not change, but the s reference points to the new object.

That’s why it’s important not to use + concatenation in situations where strings are concatenated frequently, because it creates objects frequently.

The benefit of immutable classes is that they are safe, knowing that the object cannot be modified, so they can be used with confidence and are thread-safe in a multithreaded environment.

How do YOU implement an immutable class?

I have been asked this question by the interviewer. In fact, I just refer to the design of String.

The String class is final to indicate that it cannot be inherited.

A String is essentially a char array and then final, but final doesn’t limit the data inside the array, so that’s not enough.

So the value is private and does not expose the set method, so the value cannot be modified because it is inaccessible to the outside world.

Of course, there is a need to modify, such as the replace method, so you need to return a new object as a result.

To sum up, privatize variables, then do not expose the set method and return a new object even if it needs to be modified.

17. Is Java passed by value or by reference?

Java only passes by value, whether it’s a primitive type or a reference type.

The basic type is the value pass is easy to understand, the reference type is a little confusing for some of you, especially beginners.

The JVM memory is divided into stacks and heaps. local variables and method parameters are allocated on the stack, and primitive and reference types are each 4 bytes long and double 8 bytes.

The space occupied by objects is created in the heap. Reference type variables store the address of the object in the heap to access the object, so when passing, it can be understood as passing the address of the stored variable to the past, so reference type is also value passing.

18. What are generics for? What is generic erasure?

Generics can pass types as parameters, making it possible to explicitly store Object types like some collection classes without explicitly forcing conversions (before generics, only Object, then strong conversions).

And at compile time can recognize the type, type error will be reminded, increase the robustness and readability of the program.

Generic erasure refers to the fact that parameter types are erased after compilation, i.e. the generated class file has no generic information, so it is called erasure.

But there is a detail to this erasure, which is clear when we look at the code, which is as follows:

Then we’ll look at the compiled class file.

As you can see, yess has type information, so generic types written dead in code are not erased!

This also explains why generics can be retrieved by reflection, because they are not erased!

As far as generic erasure is concerned, it is for backward compatibility, because there were no generics before JDK 5, so it is important to ensure that code compiled before JDK 5 can run on later versions, and type erasure is one way to achieve this goal.

There are other ways Java can implement generic compatibility, but erasure is easier to implement.

19. What about strong, soft, weak, and fake references?

Java divides reference types into strong reference, soft reference, weak reference and phantom reference according to the length of their life cycle.

  • Strong reference: we usually new a reference to an object. When the JVM runs out of memory, it would rather throw an OutOfMemoryError that causes the program to abort than reclaim a living object with a strong reference.
  • Soft references: The lifetime of a soft reference is shorter than that of a strong reference. When the JVM decides that it is out of memory, it tries to reclaim the object to which the soft reference points. That is, it will clean up the soft reference object before the JVM throws an OutOfMemoryError.
  • Weak references: Shorter than soft references, this object will be reclaimed during GC regardless of insufficient memory. The key in ThreadLocal uses weak references and is suitable for memory-sensitive scenarios. – Virtual reference: also called phantom reference, so called because the get of virtual reference is always null, so called get lonely, so called virtual.

The only function of a virtual reference is to coordinate with the reference queue to monitor whether the referenced object is added to the reference queue, that is, to let us know exactly when the object is reclaimed.

One more thing about virtual references is that they don’t have any effect on gc collection, but see 1.8 doc

Unlike soft and weak references, virtual references are not automatically cleared by the garbage collector when queued. Objects reachable through virtual references remain in this state until all such references are removed or they themselves become inaccessible.

In simple terms, objects referenced by virtual references cannot be gc, whereas in JDK9 there is a change log:

Link: bugs.openjdk.java.net/browse/JDK-…

Before JDK9, the object referenced by the virtual reference cannot be gc until the virtual reference itself is destroyed.

I don’t have JDK9, but I do have JDK11, so I looked at 11 doc and it did change.

Looks like he deleted that part. So before JDK9, virtual references have an effect on the GC of reference objects, and later versions have no effect.

20.Integer Cache pool?

The default range is -128 to 127. The maximum value of the cache can be changed by setting the JVM -xx :AutoBoxCacheMax=

. The minimum value cannot be changed.

IntegerCache is used when an int calls integer. valueOf.

There’s nothing to do, just check if the value is in the range, and if so go to IntegerCache.

IntegerCache initializes the cache value in a static block.

So there’s an interview question, which is what Integer 127 is equal to, and anything above 127 is not equal, because it’s the same object, so of course it’s equal.

Not only Integer but also Long, but the range is written down from -128 to 127.

A Float and a Double are decimals, and there are lots of numbers to store.

Do you know the difference between Exception and Error?

Exceptions are unexpected situations that can be expected during normal application operation and should be caught by the developer and handled accordingly.

An Error is a situation that is unlikely to occur under normal circumstances, and most of these errors will cause the program to be in an abnormal and unrecoverable state, that is, to hang.

So the inconvenience doesn’t need to be caught by the developer, because in this case it doesn’t matter if you do.

Exceptions and errors inherit from the Throwable class. In Java code, only instances that inherit from the Throwable class can be thrown or caught.

By the way, I would like to mention the attention point of exception handling, which was summarized in the previous article:

  1. Try not to catch generic exceptions like Exception, but rather specific exceptions.

Software engineering is a collaborative art, and in daily development we have the obligation to make our code more intuitive and clear about the information we want to express.

But if you use Exception for everything, other developers won’t know at a glance what the code is actually trying to catch, and the code will catch exceptions that you might want it to throw but didn’t.

  1. Don’t swallow anything unusual

What happens if we catch an exception, do not throw it, or do not write to the log? There’s no information on the line except for bugs that don’t make sense, and you don’t know what went wrong or why.

This may make a simple bug difficult to diagnose, and some students prefer to use E.printStackTrace () after catch. This method is not recommended in our products. In general, it is ok, but the output of this method is a standard error stream.

For example, in a distributed system, an exception occurs but stackTrace cannot be found.

Therefore, it is best to input it into the log. Our products can customize certain formats to input detailed information into the log system, which is suitable for clear and efficient error detection.

  1. Do not delay handling exceptions

Let’s say you have a method that takes a name, and it calls several other methods inside, and your name actually passes null, but instead of dealing with that when you go into this method or this method starts, you just pop this null pointer after you call several other methods.

In this case, your error stack will only need to throw a little bit of information to locate the error, and after many methods it will be a stack of information.

  1. Only try-catch where you want to try-catch, try-catch as small as possible

Don’t try a block of code indiscriminately as long as necessary sections of code use a try-catch, because code in a try-catch can affect how the JVM optimizes code, such as reordering.

  1. Do not use exceptions to control program flow

Do not use exceptions if you can use if/else conditional statements to determine values such as null, etc. Exceptions are definitely less efficient than some conditional statements, such as CPU branch prediction optimization, etc.

Also, every Exception instantiated takes a snapshot of the stack, which is a relatively heavy operation and cannot be ignored if there are too many.

  1. Do not handle return values ina finally block or return directly

Returning or handling a return value in finally can cause weird things to happen, such as overwriting a return ina try, or masking an exception.

22. Deep copy and shallow copy?

Deep copy: A full copy of an object, including the primitive and reference types, and a copy of the referenced objects in the heap.

Shallow copy: Only basic types and references are copied, and the referenced objects in the heap are shared with the copied objects.

So if there is a list between the copied object members, there are two lists in the heap after the deep copy, and there is still only one list in the heap after the shallow copy.

Let’s say we have a teacher object, and then we have a list of students in the members.

So deep copy is safe, shallow copy if there is a reference object and the original and copy object changes the value of the reference object will affect each other.

What is the difference between object-oriented programming and procedural programming?

Object Oriented Programming (OOP) is a Programming paradigm or style.

Organize code with classes or objects as basic units, and use the abstractions of encapsulation, inheritance, and polymorphism as code design guides.

Process oriented programming is a process as a basic unit to organize the code, process is actually the action, corresponding to the code is the function, process oriented function and data are separated, data is actually member variables.

Object-oriented classes, in which data and action go together, are a significant difference.

If these two concepts are still relatively vague, you can read this article I wrote, more than 4800 words about object-oriented and process-oriented, after reading surely understand! Object-oriented and procedural parsing

24. What is the difference between overloading and overwriting?

Overloading: a method whose name is the same, and whose parameters are of different types or order or number, does not have anything to do with the return value. The signature of the method is the name and parameter list, not the return value.

Override: When a subclass overrides a method of its parent class with the same name and argument list, that is, the method signature is consistent. Overridden subclass logic throws an exception that is the same or a subclass of the parent class exception, and the method has no less access than the parent class.

The simple understanding is that the son should not exceed the father, to respect the old and love the young.

25. What is an inner class and what does it do?

An inner class, as its name implies, is a class defined inside a class, divided by position: defined in the position of a member variable, is a member inner class, defined in a method, is a local inner class.

Static inner class if modified static, and anonymous inner class.

In general, only member inner classes, static inner classes, and anonymous inner classes are used.

Member inner classes can use all member variables and methods of external classes, including private ones.

A static inner class can only use static member variables and methods of an external class.

Anonymous classes are often used as callbacks, and when used, concrete logic is implemented to perform the callbacks.

An inner class is actually a compile-level concept, like a syntactic sugar, that is promoted by the compiler to an outer top-level class, no different from an outer class, so there is no such thing as an inner class in the JVM.

In general, a non-static inner class has no relation to the inner class and is used exclusively by the external class. It is also convenient to call the member variables and methods of the external class.

A static outer class is essentially a top-level class that can be used independently of the outer class, so it’s more about indicating the class structure and namespace.

26. What about Java collection classes?

Ask questions like this in general and wait for the interviewer to dig deeper.

Common collections include List, Set, Map, Queue, etc.

List common implementation classes are ArrayList and LinkedList.

  • ArrayList is implemented based on dynamic arrays, supports random access to subscripts and is not friendly to deletion.

  • LinkedList is implemented based on a two-way LinkedList. It does not support random access and can only be traversed sequentially, but supports O(1) insertion and deletion of elements.

Common implementation classes of Set are: HashSet, TreeSet, LinkedHashSet.

  • A HashSet is a HashMap that supports O(1) queries.

  • TreeSet is implemented based on red black tree and supports range query, but the search time complexity based on red black tree is O(LGN) and ordered.

  • LinkedHashSet, which has more than a HashSet, is a two-way list that ensures order.

Common Map implementation classes include HashMap, TreeMap, and LinkedHashMap

  • HashMap: Implement based on hash table, support O(1) query, unordered.

  • TreeMap: Red-black tree based implementation, O(LGN) query, ordered.

  • LinkedHashMap: Also more two-way linked list, support order, can be a good support for lRU implementation.

To implement lRU, set it up in order and override the removeEldestEntry method in LinkedHashMap.

One thing to mention here is that if you’re familiar with something you need to throw it in the right place. For example, by LinkedHashMap you can also extend to LRU, which shows that you have researched LinkedHashMap and know LRU, and the interviewer may not even know that you have something.

And the interviewer will basically ask about THE LRU and then extend it, for example to the improved LRU, the LRU in the mysql cache, etc., which is to move the question area to a place you are familiar with through your guidance. Isn’t that beautiful? If you’re not familiar with it, lose the bb.

Common implementation classes of Queue are LinkedList and PriorityQueue.

PriorityQueue: a heap-based PriorityQueue, which is essentially an array.

Basically can’t answer so full, a little bit about a few may be interrupted, and then dug deep, then can only see recruit open recruit.

27. What about ThreadLocal?

The essence of ThreadLocal is to localize resources to avoid sharing. In other words, each thread has its own local privatized variable, so that each thread can access its own attributes, avoiding the consumption of locks caused by multi-thread contention.

The specific relationship is shown in the figure below:

ThreadLocalMap uses linear probing to resolve hash collisions, so keep an eye on the number of ThreadLocal’s because conflict resolution is inefficient.

ThreadLocal as a key in an Entry is a weak reference, so when the strong external reference to ThreadLocal disappears, only the weak reference ThreadLocal will be cleared by GC, and the value in the Entry will remain. But you can’t access it anymore, so it’s called a memory leak.

However, when the get and set methods are called, if the direct hash is not in, the linear probe will be cleaned up only when the node whose key is null is encountered.

A better way would be to display it and call remove when it’s done, so it can be cleaned up in time.

Why weak references when weak references can cause memory leaks?

First, if the key does not have a weak reference, then when the external strong reference to ThreadLocal disappears, the thread will still exist because ThreadLocalMap is a member of the thread. If ThreadLocalMap is there, then Entry must be there, and Entry must be there for keys and values that are so strongly referenced.

So if the key is not weakly referenced, then none of the keys will be GC.

So if the key uses weak references, then at least the memory for the key can be saved, and linear detection can clear some entries.

It’s not that keys are weak references at all, because they belong to the same thread, so they can’t be GC while the thread is alive, and the chain of references can’t be changed.

And now it’s all thread pools, so threads can live for a long time, so they pile up and they get full.

So that needs to be clear.

There’s also an InheritableThreadLocal associated with that

Use InheritableThreadLocal instead of using threadLocal to pass a parent thread’s threadLocal to a child thread.

The principle is very simple. Thread already contains this member:

When the parent thread creates the child thread, the child thread’s constructor can take the parent thread and determine if the parent thread’s InheritableThreadLocal has any value, and if so, copy it over.

Note that the InheritableThreadLocal values are copied only when the thread is created, and the child threads are not affected by what happens when the parent thread changes.

That’s it for ThreadLocal.

28. What are the differences between synchronous, asynchronous, blocking, and non-blocking I/OS?

There is an operating system between our programs and our hardware, and for security reasons, Linux is divided into user mode and kernel mode.

In this context, we specify two steps for the program to read data from disk (network card) :

  1. Data is copied from the storage device to the kernel cache
  2. The data is then copied from the kernel cache into user space

Ok, now we can look at these concepts.

  • Synchronous I/O: indicates that the thread waits for 2 to complete.
  • Asynchronous I/O: the thread does not need to wait for 2 to execute.
  • Blocking I/O: Indicates that step 1 will block. That is, the thread needs to block until step 1 completes.
  • Non-blocking I/O: Indicates that step 1 is not blocked and does not need to be blocked.

So there’s also synchronous blocking I/O, or synchronous non-blocking I/O, which is a combination of steps 1 and 2.

Let’s understand it again. After all, this series is not reciting, but understanding.

Synchronous and asynchronous refers to whether you need to wait for the method call to complete.

The two concepts mainly focus on the call method, synchronous and asynchronous call encoding is different, synchronous is actually a path to write, asynchronous is the need for callbacks, events and other ways to achieve the logic behind.

Blocking and non-blocking: generally used in the underlying system call, blocking means that the thread will be blocked and enter the sleep state, that is, the CPU will be released before the time slice is up, non-blocking means that the calculation does not meet the condition and will return directly.

So the block is really blocked, it’s waiting for data, it’s giving up the time slice.

The synchronized thread actually has a time slice, so synchronization usually has a timeout period, after which it will return to the execution of the following code.

29.BIO, NIO, AIO?

BIO stands for synchronous blocking I/O, and I think it’s pretty clear from problem 28 that synchronous blocking is waiting.

In this model, you can only use one thread for each connection, and the server cannot handle so many threads if there are many connections and many concurrent connections.

NIO stands for synchronous non-blocking I/O, better known as I/O multiplexing, and is suitable for scenarios where there are many connections and each transfer is short.

AIO refers to asynchronous I/O. It is called and then ignored. The callback method is automatically executed when the data comes in.

Asynchronism can effectively reduce the waiting time of the thread and reduce the waiting time of the user thread to copy data, which is more efficient.

30. What’s new in JDK8?

Some of the more important features that are often asked about in JDK8 are as follows:

  • It replaces the permanent generation with a metaspace.
  • Lambda expressions were introduced.
  • Date classes, interface default methods, static methods were introduced.
  • Added the Stream interface

And then I’m sure you have some preparation for HashMap and ConcurrentHashMap, so toss

  • [Fixed] implementation of HashMap and ConcurrentHashMap

  • Added CompletableFuture, StampedLock and other concurrent implementation classes.

For example, some middleware asynchronous code is implemented with CompletableFuture, so you still have to do some understanding, if you are not familiar with this one.

31. Which Java concurrency utility classes have you used?

Semaphore, CyclicBarrier, and CountDownLatch.

Of course JUC below still have quite much, anyway a few say line, when interview avoid by all means do not want to throw what know all in one’s head out, this call blank.

Semaphore

These are called semaphores and are widely used in various operating systems. They allow multiple threads to access a critical region simultaneously, as opposed to Lock and synchronized, which allow only one thread to access a critical region on a given day.

If the number is less than 0, the current thread is blocked and moved to a blocking queue. Otherwise, execution is allowed.

When a thread finishes, it increments the number and wakes up a waiting thread in the blocking queue.

In fact, there is an internal Sync class that inherits from AQS and implements functionality by relying on AQS encapsulation.

Mainly used for flow control, such as parking lots only allow a certain number of parking Spaces.

A simple example is as follows:

int count; final Semaphore semaphore = new Semaphore(1); Void addOne() {try {semaphore.acquire(); // down, count+=1; } catch (InterruptedException e) { e.printStackTrace(); } finally { semaphore.release(); // Up, count + 1}}Copy the code

CyclicBarrier

According to the name, it’s a recyclable barrier.

A barrier means that once you have a group of threads running to the same barrier point, the thread blocks and waits for all threads to reach the barrier point before all threads can continue.

Here’s how to use it:

The results are as follows:

It actually does this based on the encapsulation of ReentrantLock and Condition.

I’ll dictate the rationale, because the interviewer is likely to ask the rationale.

The number of threads reaching the barrier is set first. When the thread calls await, the counter is decrement by one. If the counter decrement by one is not equal to zero, the thread calls condition.

If the counter minus one equals zero, the last thread has reached the barrier, so barrierCommand is executed if there is a barrierCommand, and condition. SignalAll is called to wake up the waiting thread, reset the counter, and start the next generation.

Source code I will not stick, suggest that they see, it is not difficult, count a big push annotations are less than 500 lines, the core method on 60 lines.

For loops, take a look at this code to understand:

When a specified number of threads reach the barrier, the count is reset and the next generation is turned on, so the CyclicBarrier can be recycled.

CountDownLatch

This lock is similar to a CyclicBarrier in that it waits for a node to arrive, but not quite the same.

A CyclicBarrier is a process in which each thread waits for a node to be blocked before all threads continue executing.

CountDownLatch is a thread that blocks while waiting for another thread to reach a node.

The following is an example:

The results are as follows:

How it works: Another Sync class inherits from AQS, the core of which is essentially an integer state.

Initialize the value of state. A call to countDown will subtract the value of state by one, and when the value of state drops to zero will wake up the thread that called await.

Mainly depends on AQS encapsulation, so the code is very few, the principle is very clear and simple.

StampedLock

Since question 30 mentioned this, and I’ve written about it before, it’s a good time to talk about it.

Think of it as an “improved” version of the read-write lock. Read/write lock Read/write is mutually exclusive, and StampedLock combines pessimistic and optimistic reads. Pessimistic reads and writes are mutually exclusive, but optimistic reads are not.

Take a look at the official example to make it clear:

class Point { private double x, y; private final StampedLock sl = new StampedLock(); void move(double deltaX, double deltaY) { // an exclusively locked method long stamp = sl.writeLock(); // get write lock try {x += deltaX; y += deltaY; } finally { sl.unlockWrite(stamp); }} double distanceFromOrigin() {// A read-only method long stamp = sl.tryoptimisticread (); // Double currentX = x, currentY = y; if (! Sl.validate (stamp)) {stamp = sl.readlock (); CurrentX = x; currentX = x; currentX = x; currentY = y; } finally { sl.unlockRead(stamp); SQRT (currentX * currentX + currentY * currentY); } void moveIfAtOrigin(double newX, double newY) { // upgrade // Could instead start with optimistic, not read mode long stamp = sl.readLock(); Try {while (x == 0.0&&y == 0.0) {long ws = sl.tryconvertToWritelock (stamp); // Upgrade to write lock if (ws! = 0L) { stamp = ws; x = newX; y = newY; break; } else { sl.unlockRead(stamp); stamp = sl.writeLock(); } } } finally { sl.unlock(stamp); }}}Copy the code

Optimistic lock is to get a judgment, if it is modified then upgrade to pessimistic lock.

However, StampedLock is non-reentrant and condition is not supported. And if a thread gets a lock with writeLock() or readLock() and is interrupted () before it finishes executing, this can cause CPU spikes. Either readLockInterruptibly or writeLockInterruptibly is required.

32. What blocking queues are used in Java?

Blocking queues are mainly used to block queue insertion and fetch operations, and block queue insertion operations when the queue is full until the queue is empty. When the queue is empty, the fetch operation of the queue is blocked until the queue has a value.

It is often used to implement producer and consumer scenarios, and is more common in pen tests.

Common examples are ArrayBlockingQueue and LinkedBlockingQueue, which are bounded blocking queues based on arrays and linked lists, respectively.

Both principles are based on ReentrantLock and Condition.

What’s the difference between bounded blocking queues?

ArrayBlockingQueue is an array-based, internal implementation that uses only one lock and can specify fair or unfair locks.

LinkedBlockingQueue is based on a linked list. The internal implementation uses two locks, one for take and one for put, so joining and unqueuing can be done in parallel, which should be more concurrent than ArrayBlockingQueue.

There’s PriorityBlockingQueue and DelayQueue, which are unbounded blocking queues that support priority and unbounded blocking queues that support delayed fetching, and if you look at the DelayQueue implementation you’ll see that PriorityQueue is internally used.

There is also SynchronousQueue, LinkedBlockingDeque, and LinkedTransferQueue.

SynchronousQueue, as mentioned in the thread pool analysis above, does not take up space. Queuing is like waiting for an outqueue, in which the producer must wait for the consumer to pick up the goods and cannot queue the goods in advance.

LinkedBlockingDeque is a double-ended blocking unbounded queue, where both the head and tail of the queue can operate, and both the head and tail can be inserted and removed.

LinkedTransferQueue, compared with other blocking queues, has the Transfer function. In fact, it is not a magic function. Generally, blocking queues queue elements, and then consumers get elements from the queue.

The transfer of LinkedTransferQueue is to check whether there are already consumers waiting when the element joins the queue. If there are consumers waiting, it can be directly given to the consumers. Therefore, there is a layer missing and no lock operation.

33. Which atomic classes have you used in Java?

Atomic classes are a series of thread-safe atomic operation classes that JUC encapsulates and implements in a lock-free manner.

The atomic classes in the above screenshot are mainly divided into five categories. Let me draw a brain map to summarize them:

The core principle of atomic classes is based on CAS (Compare And Swap).

CAS is simply understood as: a shared variable is given a memory address, and then the expected value (expected value) and the new value in memory are compared. Then a CPU instruction is used to compare whether the value on the memory address is equal to the expected value. If yes, the value on the memory address is replaced with the new value, if not, it is not replaced and replaced.

That is to say, the hardware layer supports a single instruction to perform these operations, and a single instruction cannot be interrupted, so atomicity is guaranteed.

Basic types of

It can be easily interpreted as the basic types AtomicBoolean, AtomicInteger, and AtomicLong are thread-safe and atomically updated.

An array type

AtomicIntegerArray, AtomicLongArray, and AtomicReferenceArray can be atomically updated for each element in the array. The difference between them is nothing more than the type of data stored in the array.

Reference types

AtomicReference, AtomicStampedReference, and AtomicMarkableReference are atomized updates of object references.

The difference is that AtomicStampedReference and AtomicMarkableReference avoid THE ABA problem of CAS.

An AtomicStampedReference is avoided by stamp version number, and an AtomicMarkableReference is avoided by mark, a Boolean value.

ABA problem

Because the CAS is the value on the expectations and the memory address, assumes that the expected value is 1, the address on the value is 1, now just in the middle was changed to 2, then changed back to 1, so at this point you CAS operation to contrast can be replaced, if you don’t know the intermediate values to turn, this situation is called “ABA” problem.

The solution to the ABA problem is to use the version number and add +1 each time you change the version, so that even if the value is the same but the version is different, you know that it was changed before.

Property update type

AtomicIntegerFieldUpdater, AtomicLongFieldUpdater and AtomicReferenceFieldUpdater is through reflection, atomized update the object’s properties, The attribute must be volatile to ensure visibility.

accumulator

These are all updates, and DoubleAccumulator, DoubleAdder, LongAccumulator, and LongAdder are used to accumulate data.

First of all, AtomicLong can also accumulate, while LongAdder is a professional accumulate and can only accumulate, with higher concurrency. It reduces thread contention by dividing multiple cells to improve concurrency.

You can understand that if you take AtomicLong, it is a book, and then 20 people have to make the book add up the count.

LongAdder has 10 books, which 20 people can use to count each book (reducing competition and improving concurrency), and then add up the results from the 10 books.

XxxAccumulator is different from xxxAdder.

XxxAccumulator ((x, y) -> x + y, 0L}) is an accumulator (x, y). XxxAccumulator is an accumulator (x, y).

So you can say that xxxAdder is a special case of xxxAccumulator.

34. Difference between Synchronized and ReentrantLock?

Synchronized and ReentrantLock are both reentrantlocks, and ReentrantLock requires manual unlocking while Synchronized does not.

ReentrantLock allows you to set a timeout period to avoid deadlocks. ReentrantLock is flexible and supports fair locking, interruptible, and conditional judgment.

Synchronized does not support timeout, non-fair, non-interruptible, and does not support conditions.

In general, Synchronized is sufficient and relatively simple in general cases, while ReentrantLock is more flexible and supports more functions, so ReentrantLock is used in complex cases.

As for Synchronized not being as good as ReentrantLock, that was years ago.

35. The principle of Synchronized?

The principle of Synchronized is based on a lock object and a Monitor object associated with it.

When biased locks and lightweight locks only need to use CAS to control the lock object head to complete the unlock action.

After upgrading to heavyweight locks, you also need to leverage monitor objects, using CAS and MUtex as the underlying implementation.

The thread that does not compete for the lock is stored in the wait queue, and the thread that obtains the lock is stored in the conditional wait queue after invoking wait. Unlock and notify will wake up the waiting threads in the corresponding queue to compete for the lock.

However, because blocking and wake up depend on the implementation of the underlying operating system, the system call has a switch between user state and kernel state, so it has a high overhead, so it is called heavyweight lock.

So there will be biased locking and lightweight locking optimization, and the introduction of adaptive spin mechanism, to improve the performance of the lock.

I’ve actually written two articles about Synchronized, and after reading them you can tell your interviewer that you’ve read the JVM source code, literally, because it was analyzed at the source level.

It also points to an idea that is almost universally wrong on the Internet and a common cognitive error.

All in all, Synchronized is basically better than many people after watching it.

Synchronized in-depth JVM analysis

Synchronized went up to heavyweight and couldn’t get off? You are wrong!

36. How does ReentrantLock work?

ReentrantLock is a ReentrantLock based on AQS, which supports both fair and unfair methods.

The internal implementation relies on a state variable and two wait queues: synchronous queues and wait queues.

Use CAS to modify state to scramble for locks.

If not, it will wait in the synchronization queue, which is a bidirectional linked list.

Condition condition does not meet the waiting queue waiting, is also a two-way linked list.

The difference between a fair lock and not a fair lock is whether the thread acquires the lock by joining the end of the synchronization queue or by directly contending for the lock with CAS.

This is the thing, it should not be difficult to understand, I worry about the graph, hey hey.

37. What about AQS?

The principle of AQS is actually mentioned above, so I won’t repeat it here.

If the interviewer asks you why you need AQS, say so.

AQS encapsulates some operations, such as basic methods such as joining a queue, exposing methods for use by other associated JUC locks.

CountDownLatch, Semaphore, etc.

It acts as an abstraction and encapsulation.

38. Do you know the read/write lock?

The default read/write lock in Java is ReentrantReadWriteLock.

The read-write lock has two locks, namely read lock and write lock.

All but read operations are mutually exclusive.

So if you read a lot and write a little, use read/write locks.

It is also important to note that if this is not the case, do not use read-write locks, because read-write locks require additional maintenance of read-lock state, so it is better to use regular locks if there are not many read-write operations.

Read/write lock is also implemented based on AQS. More specifically, state is divided into two parts. The high 16bit is used to identify the read state and the low 16bit is used to identify the write state.

This neatly implements two locks in one state, heh heh.

39. Does CAS know?

CAS is compare and swap.

So, for example, we often have a summation requirement, comparing whether a value is equal to 1, if it is equal to 1 we replace it with 2, if it is equal to 2 we replace it with 3.

This comparison is not safe in the case of multiple threads, such as when two threads perform the comparison to see if the value is equal to 1, and then both threads find that the value is equal to 1.

And then both threads change it to 2, so you add it twice, but it’s equal to 2.

In fact, this situation can be solved by locking, but locking is more resource consumption.

So the hardware side supports this, encapsulates the comparison and swap action as an instruction, so that atomicity is guaranteed, there is no determination that the value is really equal to 1, but the value of the substitution is not equal to 1.

This instruction is CAS.

CAS requires three operands: the old expected value (1 in the figure), the variable memory address (the memory address of A in the figure), and the new value (2 in the figure).

The instruction takes the value based on the address of the variable, compares it to the expected value, if so, replaces it with the new value, if not, does not replace it.

In fact, question 33 has already mentioned this, including the ABA problem. The reason why I write it again is that I can tell the interviewer from the ideas I provided.

Don’t start with three operands.

You add up the scenarios you encounter, and then multithreading is unsafe, and then locking is bad, and then the hardware provides this instruction.

So if I’m the interviewer, I’m going to be like, hey, guys can do this.

40. What about the life cycle of threads?

It can be divided into four categories: initial state, runnable state, termination state and hibernation state.

When a thread is created, it is in its initial state and has not yet been started.

A runnable state is a state that is either running or waiting for a CPU time slice.

The dormant state is classified into three types: blocked state waiting for lock, Waitting state waiting for condition, or Timed_waitting state with time limit.

  • Object.wait, thread. join, locksupport.park ()
  • Time wait is the method above with the timeout parameter, such as Object.wait(1000).

A terminating state is when a thread finishes executing, either automatically after completing a task or when an exception is raised.

41. What is JMM?

JMM stands for Java Memory Model.

The JMM is actually a set of rules that dictate when a write from one thread is visible to another thread (JSR133).

Abstractly, the JMM divides memory into local memory and main memory, with each thread having its own privatized local memory and then main memory for shared data.

It is up to the JMM to define the rules for the interaction between the two memories.

Note that local memory is just an abstract term that actually refers to registers, CPU cache, and so on.

In short, the JMM is a virtual machine-level memory specification that abstracts from the details of the underlying hardware.

42. What about atomicity, visibility, and order?

atomic

It means that an operation will not be interrupted, either the operation will complete or it will not be executed.

visibility

When one thread makes a change to a shared variable, other threads can immediately get the latest value.

order

A rearrangement of instructions by the compiler or processor that can affect the execution order of multiple threads and cause errors.

43. What is a common Java garbage collector?

I don’t really want to write the answer to this question, the content is quite rigid, I suggest you read “In-depth understanding of the JVM virtual machine”, but that book does not cover the ZGC in detail.

And ZGC, I did write one, you can have a look.

Meituan interviewer asked me: what does ZGC stand for?

44. Garbage collection, how to determine whether an object is garbage?

There are two methods, reference counting and reachability analysis.

Reference counting has a loop-dependent problem, but it can be fixed.

Reachability analysis starts with the root reference (GCRoots) and traverses the reference chain. If the object is reachable, it is alive. If not, the object has become garbage.

Root references include global variables, on-stack references, registers, and so on.

As I’ve written before, read this article in detail, and after reading it, this is a great area for interviews, and it’s already gone beyond many interviewers.

Take a deep look at the bottom of garbage collection, this time to get you to the bottom of it

45. What garbage collection algorithms do you know?

The common ones are: copy, mark-clean, and mark-tidy.

Mark-clear

The mark-sweep algorithm should be the one that best matches the way we think about garbage in the first place.

For example, if we want to remove the garbage in our room, we must first locate the garbage (corresponding to mark) and then throw it away (corresponding to remove). Simple and rude, I don’t care about the remaining things that are not garbage.

However, this algorithm has a drawback:

  1. Space debris, which makes it impossible for larger objects to apply for more contiguous space when you have plenty of space. This leads to another GC.

Replication algorithm

The rough copy algorithm is to split the space in two and copy the surviving objects on one side to the other side, so there is no space fragmentation problem, but the memory utilization is too low, only 50%, so HotSpot divides a space into 3 pieces, one Eden and two Survivor.

Since most of the new generation of objects are short-lived, not many will survive, so the default space division ratio is 8:1:1.

The usage is to use only Eden and one Survivor at a time, and then throw the surviving objects into the other Survivor. Then clean Eden and the previous block Survivor. Eden and Survivor are then used to welcome the new object. This is equal to swapping two survivors each time a collection is collected.

Mark-collation algorithm

The idea of the mark-Tidy algorithm is the same as the mark-clean algorithm. The first step is to mark the objects that need to be cleaned, but the next step is not the same, it is to tidy, yes, like the above said that those who clean up the garbage in the room every time so diligent.

All living objects are moved each time, and they are arranged in the order of memory address, that is, the living objects are moved like one end, and then all the memory after the end memory address is reclaimed. So there’s no space debris problem with it.

Come on. I wrote it earlier. It’s almost done.

Blast! Asked me 18 JVM questions in one go!

46. What is the difference between String, Stringbuffer, StringBuilder?

String is a fundamental and important class in Java, and is a typical implementation of the Immutable class. String is declared final, and all properties except the hash property are declared final.

Because of its immutability, concatenating strings, for example, produces a lot of useless intermediate objects, which can have a performance impact if done frequently.

A StringBuffer is a class that solves the problem of creating too many intermediate objects when concatenating a large number of strings. It provides append and Add methods that add strings to the end of an existing sequence or at a specified location.

Its essence is a thread-safe, modifiable character sequence that adds synchronized to all methods of modifying data. But ensuring thread safety comes at a performance cost.

In many cases we don’t need thread-safe string concatenation. This is where StringBuilder comes in. StringBuilder is released in JDK1.5 and is essentially the same as StringBuffer in that it removes thread-safe parts and reduces overhead.

Both StringBuffer and StringBuilder inherit from AbstractStringBuilder, using modifiable char arrays (byte arrays after JDK 9) at the bottom.

So if we have a lot of string concatenation, if we can predict the size, it is best to set capacity in the new StringBuffer or StringBuilder to avoid the overhead of multiple capacity expansion.

Expansion to discard the original array, but also copy the array to create a new array.

Happens-before?

This question probably comes from understanding the Java Virtual Machine.

Happens-before are rules defined so that some actions take place before others in a given situation.

A precedes B, which means that the result of A can be obtained when B begins. The point is not that A is executed earlier than B, but that the result of A can be read by B when B starts.

This is ordered by the JVM, and you could argue that programmers writing JVMS need to implement JVMS according to these rules.

If the action meets the following rules, the action is defined as follows.

  • Sequence rule: In a thread, in order of program code, operations written earlier take place before those written later. To be precise, it should be the control flow sequence rather than the program code sequence, since branches, loops, and so on are to be considered.
  • Pipe lock rule: an UNLOCK operation occurs first when a subsequent lock operation is performed on the same lock. It must be emphasized here that the same lock, and “behind” refers to the chronological order.
  • Rule for volatile Variables: Writes to a volatile variable occur first after reads, again in chronological order.
  • Transitivity rule: If operation A precedes operation B and operation B precedes operation C, it follows that operation A precedes operation C.
  • Thread start rule: The start() method of the Thread object occurs first for each action of the Thread.
  • Thread interrupt rule: A call to the interrupt() method occurs when the interrupted Thread’s code detects that an interrupt has occurred. This can be detected by thread.interrupted ().
  • Thread termination rule: All operations in a Thread occur before the Thread terminates. We can detect that the Thread has terminated by the end of thread.join () method, the return value of thread.isalive (), and so on.
  • Object finalization rule: The completion of an object’s initialization (completion of constructor execution) occurs first at the start of its Finalize () method.

48. What is the adaptive spin of a lock?

This is the Syncronized spin of the heavyweight lock.

In heavyweight locks, a thread will spin if it fails to compete for the lock. In other words, it will idle the CPU waiting for the lock to be released.

The spin is optimized to a certain extent because in some cases the lock can be released as soon as the thread is blocked, which is expensive.

It’s like the difference between idling and stalling. If you have to wait a long time (you can’t get the lock for a long time), then stalling is worth it.

Idling (spin) is cheaper if you’re going to get the lock later.

But since this spin number is hard to judge, adaptive spin is introduced.

To put it bluntly, as a rule of thumb, if the last spin took a little while to get the lock, this time it takes a few more spins, and if the last spin took a long time to get the lock, this time it takes a little less spin.

This is called adaptive spin of the lock.

49.JVM memory region partitioning

Java virtual machine runtime data is divided into program counters, virtual machine stacks, local method stacks, heaps, and method areas.

The three areas of the program counter, virtual machine stack, and local method stack are thread private and are automatically reclaimed when a thread dies, so there is no need to manage them.

The heap and method areas are shared by threads, so the garbage collector focuses on these two areas.

The heap only holds objects that are normally new.

The method area holds loaded type information, just-in-time (JIT) compiled code, and so on.

50. Do you understand the order reordering?

To improve the efficiency of program execution, the CPU or compiler will execute the command reorder.

The reason is that memory accesses are much slower than the CPU, so you need to order execution so that the CPU doesn’t sit idle because of slow instructions that access memory.

CPU execution has the idea of an instruction pipeline, and branch prediction, and so on, and I wrote an article about this earlier, and you can look at CPU branch prediction

In order to improve efficiency, there will be instruction rearrangement, resulting in out-of-order execution of the situation, but it will ensure that the result is consistent with the single-thread execution result, this is called as-IF-serial.

Multithreading, however, is not guaranteed. The volatile keyword in Java prevents instruction rearrangements before and after modifying variables.

51. Is visibility guaranteed for final and?

Can’t.

You might see some answers that say you can guarantee visibility, but that’s not what we mean by visibility.

In general, we mean that when one thread changes a shared variable, another thread can immediately see the change and get the newly changed value.

Final does not guarantee this; volatile does.

The value of the final field can be seen by other threads after the constructor completes initialization, and the value of the final field is not passed.

Without the final modifier, it is possible that writes to fields in the constructor are sorted externally so that no other thread can get the value after the write.

Let’s take a look at the code to make it clear.

public class YesFinalTest {
   final int a; 
   int b;
   static YesFinalTest testObj;

   public void YesFinalTest (a) { // Assign a value to the field
       a = 1;
       b = 2;
   }

   public static void newTestObj (a) {  // Thread A calls this method
       testObj = new YesFinalTest ();
   }

   public static void getTestObj (a) {  Thread B then executes the method
       YesFinalTest object = obj; 
       int a = object.a; // It must be 1
       int b = object.b; // It is possible to read 2 here}}Copy the code

For final domains, the compiler and processor follow two reordering rules (see InfoQ Chen xiaoming) :

  1. There is no reordering between a write to a final field within a constructor and a subsequent assignment of a reference to the constructed object to a reference variable. Read an contain for the first time
  2. There is no reordering between a reference to an object ina final field and a subsequent first read of the final field.

So that’s the visibility of final, and that visibility is not the same thing as what we call visibility in concurrency!

So final cannot guarantee visibility!

52. How to optimize locks?

Pay attention to the granularity of the lock, do not define the lock directly in the method periphery, the lock code block as small as possible, such as double-check lock is a typical optimization.

Different scenarios define different locks and cannot be handled by a single lock. For example, read/write locks and copy while writing can be used in scenarios with more reads and less writes.

For more details, see what I wrote earlier about Java locks.

The last

This series is also synchronized to my Github above, if there is any error, welcome to comment, or mention issues, PR ha.

Also welcome to wechat search [yes training level guide] follow me.

Java foundation for the time being to write so much, and then have to write a lot, ha ha long way, feel very full!


I’m yes, from a little bit to a billion bits. See you next time.