Virtual Machine Foundation
JVM Reference Articles
JVM Memory Management
Java source code files (.java) are compiled by the Java compiler into bytecode files (.class), and then the class loader in the JVM loads the bytecode files for each class. When loaded, the bytecode files are delivered to the JVM execution engine for execution.
Runtime data areas are divided into thread private data areas and thread shared data areas:
Thread private data area contains: program counter, virtual machine stack, local method stack Thread shared data area contains: Java heap, method area (internal contains constant pool)
The thread-private data area contains:
- Program counter: An indicator of the line number of bytecode executed by the current thread
- Virtual machine stack: is the memory model for Java method execution
- Local method stack: Native method service used by the VM
The thread shared data area contains:
-
Java heap: to hold almost all object instances and arrays; Is the main area that the garbage collector manages, also known as the “GC heap”; Is the largest chunk of memory managed by the Java Virtual Machine
-
Method area: used to store data such as class information, constants, static variables, and code compiled by the real-time compiler that has been loaded by the virtual machine. In addition to the Class version, field, method, interface, and other description information, there is also a Constant Pool (Constant Pool Table) information, which is used to store a variety of literal and symbolic references generated at compile time, this part of the content will be stored in the method area of the runtime Constant Pool
The difference between Java heap and stack
-
The heap memory is used to store objects and arrays in Java. When we create an object or an array, we create a space in the heap memory for it to store. Features: first in, first out, last in, last out. The heap can dynamically allocate memory size, and since memory is allocated dynamically at run time, access is slow.
-
Stack memory
These are used to execute programs, such as primitives and reference variables for objects. Features: first in first out, last in first out, access speed faster than heap, second only to register, stack data can be shared, but the disadvantage is that the size and life of the data in the stack must be determined, lack of flexibility
Garbage collection mechanism/collection algorithm
There are two ways to determine whether an object is recyclable:
-
Reference counting algorithm: add a reference counter to an object, incrementing the counter value every time it is referenced in a place; When the reference is invalidated, the counter is subtracted by 1; An object whose counter is zero at any time is no longer usable. However, reference counting algorithm is not used to manage memory in the mainstream Java virtual Machine, mainly because it is difficult to solve the problem of circular reference between objects, so another object survival determination algorithm appears.
-
Reachability analysis: Starting from a series of objects called “GC Roots”, search down from these nodes. The path taken by the search is called a reference chain. When an object is not connected by any reference chain to the GC Roots, it proves that the object is unavailable. Objects that can be used as GC Roots: objects referenced in the VIRTUAL machine stack, mainly refer to local variables in the stack frame, objects referenced by Native methods in the local method stack, objects referenced by class static attributes in the method area, and objects referenced by constants in the method area
Recovery algorithm
Generational collection algorithm: it is an algorithm used by current commercial virtual machines to divide the Java heap into new generation and old generation according to the different life cycle of objects, and adopt the most appropriate collection algorithm according to the characteristics of each generation.
-
Cenozoic: Most objects die, few survive. Using the “copy algorithm”, you only need to copy a small number of living objects.
- Copy algorithm: The available memory is divided into two equal sized pieces according to capacity, and only one of them is used at a time. When this piece of memory is exhausted, the surviving objects are “copied” to another piece of the above, and then this piece of memory space is cleared once.
-
Old age: object survival rate is high. Using either the mark-clean or mark-clean algorithm, you only need to mark fewer reclaimed objects.
- Mark-clear algorithm: first “mark” out of all the objects to reclaim, and then unified “clear” all the marked objects.
- Tag-collation algorithm: first “mark” out of all the objects that need to be recycled, and then “collate”, so that the living objects are moved to one end, and finally directly clean up the memory outside the end boundary.
Refer to the article
Java based
Java reference Types
-
A StrongReference: an object with a StrongReference is not GC. Even if there is insufficient memory space, the JVM would rather throw an OutOfMemoryError and terminate the program abnormally than arbitrarily reclaim objects with strong references.
-
SoftReference: if an object has a SoftReference, it will be GC when the memory space is insufficient. Soft references are often used to implement memory-sensitive caching.
-
WeakReference: objects associated with weak references are GC regardless of whether the current memory is sufficient. Strength is weaker than soft references and is often used to describe non-essential objects; Often used to resolve memory leaks (Context in Handle)
-
PhantomReference: Objects that hold only virtual references can be GC at any time; Often used to track the activity of objects being collected by GC; It must be used in conjunction with the ReferenceQueue. When the garbage collector is ready to reclaim an object and finds that it has a virtual reference, it will add the virtual reference to the ReferenceQueue associated with the object before the memory of the object is reclaimed.
Class loading mechanism
Class loading mechanism: The virtual machine loads the data describing the Class from the Class file to the memory, verifies, converts, parses, and initializes the data, and forms a Java type that can be directly used by the virtual machine. In addition, the loading, wiring, and initialization of the types are done while the program is running, thereby sacrificing some performance overhead in exchange for a high degree of flexibility in Java programs. Main stages:
-
Loading: Get the binary byte stream that defines a class through its fully qualified name; The static storage structure represented by the binary byte stream is transformed into the runtime data structure of the method area. The data storage data structure is defined by the virtual machine. A java.lang.Class object representing this Class is generated in memory, which will act as the external interface for the program to access these types of data in the method area
-
Verification: Ensures that the byte stream of a Class file contains information that meets the current vm requirements, including file format Verification, metadata Verification, bytecode Verification, and symbol reference Verification
-
Preparation: Allocates memory for class variables. Since variables here are allocated memory from the method area, only class variables are included, not instance variables, which are allocated to the Java heap along with the object when it is instantiated; Sets the initial value of the class variable, usually zero
-
Resolution: The process by which a VIRTUAL machine replaces a symbol reference in the constant pool with a direct reference
-
Initialization: The final step in the class loading process, which begins the actual execution of the Java bytecode defined in the class. The previous class loading process was dominated and controlled by the virtual machine except for the “loading” stage where user applications could participate through custom class loaders
The memory model
Main memory is where all variables are stored. Each thread has its own working memory, which holds copies of main memory copies of variables used by that thread. For better speed, a virtual machine may preferentially store working memory in registers and caches.
Atomicity timing in a concurrent process
- atomic
The atomic variable operations that can be guaranteed directly include read, Load, assign, use, Store, and write. Therefore, the access and read of basic data types can be considered atomic.
To ensure greater atomicity, the higher-level bytecode instructions Monitorenter and Monitorexit implicitly use the lock and unlock operations, which are reflected in Java code as the synchronized keyword.
- Visibility (when one thread changes the value of a shared variable, other threads are immediately aware of the change)
This is done by synchronizing the new value back to main memory after the variable is modified and refreshing the value from main memory before the variable is read.
Three keywords are provided to ensure visibility: volatile ensures that new values are immediately synchronized to and flushed from main memory immediately before each use; Synchronized synchronizes a variable back into main memory before performing an UNLOCK operation on it. Once a final-modified field is initialized in the constructor and the constructor does not pass a reference to this, the value of the final field can be seen in other threads.
- Orderliness (execution in order of instructions)
If observed in this thread, all operations are ordered, refers to the “thread in the performance of serial semantics”; If you observe another thread in one thread, all operations are out of order, which refers to “instruction reorder” phenomenon and “working memory and main memory synchronization delay” phenomenon.
Two keywords are provided to guarantee order: volatile itself contains semantics that prohibit reordering of instructions; Synchronized guarantees that only one thread is allowed to lock a variable at a time, so that two synchronized blocks holding the same lock can enter only sequentially.
Design Patterns (used)
-
The single responsibility principle: A class is responsible for only the corresponding responsibility in one functional domain
-
Open and Closed principle: Open for extension, closed for modification
-
The principle of dependency inversion: abstractions should not depend on details, details should depend on abstractions. In other words, program to the interface, not to the implementation
-
Demeter’s law: Interaction between objects should be minimized. If two objects do not have to communicate directly with each other, then they should not have any direct interaction. If one object needs to call a method on the other, the call can be forwarded through a third party
-
Synthesis/polymerization reuse principle: Use synthesis/polymerization whenever possible, and inheritance whenever possible
Extension: Design patterns used in Android source code, and design patterns used by yourself
View Event distribution: chain of responsibility mode BitmapFactory Load image: factory mode
ListAdapter: Adapter pattern DialogBuilder: builder pattern Adapter. The notifyDataSetChanged () : the observer pattern Binder mechanism: the proxy pattern
Often used design patterns
The singleton pattern
The primary version
public class Singleton {
private static Singleton instance;
private Singleton (a){}
public static Singleton getInstance(a) {
if (instance == null) {
instance = new Singleton();
}
returninstance; }}Copy the code
premium
public class Singleton {
private volatile static Singleton singleton;/ / code 1
private Singleton (a){}
public static Singleton getSingleton(a) {
if (singleton == null) {/ / code 2
synchronized (Singleton.class) {
if (singleton == null) {/ / code 3
singleton = new Singleton();/ / code 4}}}returnsingleton; }}Copy the code
In synchronize, two threads may enter code 2 at the same time. In synchronize, only one thread can enter the code below, while thread A enters and thread B waits outside. After thread A completes code 3 and 4, thread B enters the methods below synchronized. Thread B judgment when you code 3, however, to ensure the multi-threaded thread-safe singleton pattern under, in addition to carefully use the singleton pattern, because once a singleton pattern after initialization Only process exits could be recycled, if an object is not often used, try not to use the singleton, or to use a few times, keep the singleton is memory
Advantages:
- Only one object exists in memory, saving system resources.
- Avoid multiple resource operations, such as one file operation. Because only one instance exists in memory, avoid simultaneous operations on the same resource file.
Disadvantages:
- Singletons that hold contexts are prone to memory leaks.
- The singleton pattern generally has no interface and is difficult to extend, which can only be achieved by modifying the code.
Builder pattern
Refer to the article
Specific product categories
public class Computer {
private String mCPU;
private String mMemory;
private String mHD;
public void setCPU(String CPU) {
mCPU = CPU;
}
public void setMemory(String memory) {
mMemory = memory;
}
public void setHD(String HD) { mHD = HD; }}Copy the code
Abstract builder
public abstract class Builder {
public abstract void buildCPU(String cpu);/ / assemble the CPU
public abstract void buildMemory(String memory);// Assemble memory
public abstract void buildHD(String hd);// Assemble the hard disk
public abstract Computer create(a);// Return to the assembled computer
}
Copy the code
Author implementation class
public class ConcreteBuilder extends Builder {
// Create a product instance
private Computer mComputer = new Computer();
@Override
public void buildCPU(String cpu) {/ / assemble the CPU
mComputer.setCPU(cpu);
}
@Override
public void buildMemory(String memory) {// Assemble memory
mComputer.setMemory(memory);
}
@Override
public void buildHD(String hd) {// Assemble the hard disk
mComputer.setHD(hd);
}
@Override
public Computer create(a) {// Return to the assembled computer
returnmComputer; }}Copy the code
The caller
public class Director {
private Builder mBuild = null;
public Director(Builder build) {
this.mBuild = build;
}
// Direct the installer to assemble the computer
public void Construct(String cpu, String memory, String hd) { mBuild.buildCPU(cpu); mBuild.buildMemory(memory); mBuild.buildHD(hd); }}Copy the code
call
Director direcror = new Director(new ConcreteBuilder());// Create a command instance and assign the corresponding builder, (boss assigns tasks)
direcror.Construct("i7-6700"."Samsung DDR4"."Seagate 1 t");// Assemble the computer
Copy the code
Advantages and Disadvantages of Builder Mode
advantages
- Good encapsulation to hide internal build details.
- Easy to decouple, decouple the product itself from the product creation process, you can use the same creation process to get different products. In other words, details depend on abstractions.
- Easy to extend, concrete builder classes are independent of each other, adding new concrete builders does not need to change the original library code.
- It is easy to control the creation of objects precisely, and because the specific builder is independent, it is possible to refine the construction process without any impact on other modules.
disadvantages
- Generate redundant Build objects and Dirextor classes.
- The builder pattern creates products that generally have more in common and have similar components; If the difference between products is large, the builder pattern is not suitable, so its use is limited
Source code for example: dialog.builder
The factory pattern
Abstract product class
public abstract class Product {
public abstract void show(a);
}
Copy the code
Specific Product category
public class ProductA extends Product {
@Override
public void show(a) {
System.out.println("product A"); }}// Specific product category B
public class ProductB extends Product {
@Override
public void show(a) {
System.out.println("product B"); }}Copy the code
Create an abstract factory class
// Abstract factory class
public abstract class Factory {
public abstract Product create(a);
}
Copy the code
Create a concrete factory class that inherits from the abstract factory class
public class FactoryA extends Factory {
@Override
public Product create(a) {
return new ProductA();/ / create ProductA}}// Factory type B
public class FactoryB extends Factory {
@Override
public Product create(a) {
return new ProductB();/ / create ProductB}}Copy the code
The test code
Factory factoryA = new FactoryA();
Product productA = factoryA.create();
productA.show();
B / / products
Factory factoryB = new FactoryB();
Product productB = factoryB.create();
productB.show();
Copy the code
Advantages:
- It conforms to the principle of openness and closure. When adding a new product, only the corresponding specific product class and the corresponding factory subclass can be added.
- Consistent with the single responsibility principle. Each specific factory class is only responsible for creating the corresponding product.
Disadvantages:
- When new products are added, corresponding factory classes need to be added, and the number of system classes increases in pairs, increasing the complexity and performance overhead of the system.
Source code such as ThreadFactory
Observer mode
Refer to the article
Definition: defines a one-to-many dependency between objects, so that when the state of an object changes, all objects that depend on it are notified and automatically updated.
Remark:
-
Subject: also known as abstract observed, saves references to all observer objects in a set. Each Subject can have any number of observers. Abstract topics provide an interface for adding and removing observer objects.
-
ConcreteSubject: Also known as ConcreteSubject, stores the state in the ConcreteSubject; Notifying all registered observers of changes in the internal state of a specific topic.
-
Observer: Defines an interface for all concrete observers, updating themselves when they are notified of topics.
-
ConcrereObserver: Implements an update interface defined by an abstract observer that updates its own state when notified of topic changes.
Code implementation
Abstract observer
public interface Observer {// Abstract observer
public void update(String message);// Update the method
}
Copy the code
Specific observer
public class Boy implements Observer {
private String name;/ / name
public Boy(String name) {
this.name = name;
}
@Override
public void update(String message) {// The boy's specific reaction
System.out.println(name + ", received the message :" + message+"Pick up the delivery."); }}Copy the code
Creating an abstract theme
public interface Observable {// Abstract the observed
void add(Observer observer);// Add an observer
void remove(Observer observer);// Delete the observer
void notify(String message);// Notify the observer
}
Copy the code
Creating a specific topic
public class Postman implements Observable{/ / Courier
private List<Observer> personList = new ArrayList<Observer>();// Save the information of the recipient (observer)
@Override
public void add(Observer observer) {// Add recipients
personList.add(observer);
}
@Override
public void remove(Observer observer) {// Remove recipients
personList.remove(observer);
}
@Override
public void notify(String message) {// Notify the recipients (observers) one by one
for(Observer observer : personList) { observer.update(message); }}}Copy the code
The test code
Observable postman=new Postman();
Observer boy1=new Boy("The road");
Observer boy2=new Boy("Joe,");
postman.notify("The delivery has arrived. Please pick it up downstairs.");
Copy the code
Advantages:
- Decouple the observer from the subject. Let both sides of the coupling depend on abstractions rather than concrete ones. So that changes on one side don’t affect changes on the other.
- Easy to extend, adding observers to the same topic without changing the original code.
Disadvantages:
- When using the observer pattern need to consider the development efficiency and operation efficiency, applications include a observer, multiple observers, development, debugging and other content will be more complex, and in Java message notification is generally sequential, so an observer caton, will affect the overall execution efficiency, and in this case, generally USES the asynchronous implementation.
- Redundant data notifications may be caused.
The Observer mode in Android source code: Listener
Other Design patterns
As I dabble in less, some can only say simple understanding. Here’s a nice series of blogs to share, thanks for planting trees.
Design Pattern series tutorial!!
Source design
Interfaces and abstract classes
- Abstract classes can provide the implementation details of member methods, while interfaces can only have public abstract methods, but no implementation.
- Member variables in an abstract class can be of any type, while member variables in an interface can only be public static final.
- Interface member variables can only be static constants. There are no constructors or code blocks, but abstract classes can have them.
- A class can inherit only one abstract class, but a class can implement multiple interfaces.
Abstract classes are faster to access than interfaces, because interfaces need time to find ways to implement them in the class.
- If you add a new method to an abstract class, you can give it a default implementation. So you don’t need to change your current code. If you add methods to an interface, you must change the class that implements the interface.
So: Abstract classes emphasize reuse, interfaces emphasize decoupling.
Last updated: 13:45:40 dec 5, 2019