(1) Ali's interviewer asked me , Can you write it yourself String class

answer : may not , because Parent delegation mechanism based on class loading , Will load the parent class , Parent class found conflict String No more loading ;

(2) Can I load the class , Modify the bytecode of the class

answer : sure , use Java Probe technology , You can refer to :Java probe -Java Agent technology - Ali interview questions
<http://www.cnblogs.com/aspirant/p/8796974.html>

Reading table of contents

    What is classloader
    Class loader and class's ” identical “ judge
    Class loader type
  Parents Delegation Model
  Class loading process
  Custom class loader
  JAVA Hot deployment implementation

What is classloader

Responsible for reading Java Byte code , And convert to java.lang.Class An instance of class ;
<>
Class loader and class's ” identical “ judge

Class loaders are used to load classes , It can also be used to determine whether a class Java Uniqueness in virtual machine .

Even the same byte code , The class obtained after being loaded by different class loaders , It's different .

Generally speaking , To determine whether two classes “ identical ”, The premise is that these two classes must be loaded by the same classloader , Otherwise, these two classes do not “ identical ”.
I mean “ identical ”, Including class Class Object's equals() method ,isAssignableFrom() method ,isInstance() method ,instanceof
Keywords, etc .
 
Class loader type

Start class loader ,Bootstrap ClassLoader, load JACA_HOME\lib, Or by -Xbootclasspath Parameter qualified class
extensions class loader ,Extension ClassLoader, load \lib\ext, Or by java.ext.dirs Class specified by system variable
Application class loader ,Application ClassLoader, load ClassPath Class library in
Custom class loader , By inheritance ClassLoader realization , Generally, loading our custom classes
 
Parents Delegation Model

Classloader Java Class as other Java Class is the same , Also to be loaded by the classloader ; In addition to the boot class loader , Each class has its parent loader ( Parent-child relationship is composed of ( Not inheritance ) To achieve );

The so-called parent delegation refers to every time a class load request is received , Delegate the request to the parent loader first ( All load requests will eventually be delegated to the top-level Bootstrap
ClassLoader In loader ), If the parent loader cannot complete the load ( No corresponding class found in the loader's search scope ), Subclass tries to load itself .


Benefits of parental delegation

* Avoid multiple loading of the same class ;
* Each loader can only load its own range of classes ; Class loading process
Class loading is divided into three steps : load , connect , initialization ;

As shown below , Is the life cycle of a class from loading to using and unloading , Picture from reference ;



load

Name a class according to its full limit ( as cn.edu.hdu.test.HelloWorld.class) To read the binary byte stream of this class to JVM inside ;


Converts the static storage structure represented by the byte stream to the runtime data structure of the method area (hotspot Choose to Class Object stored in method area ,Java The virtual machine specification does not explicitly require that it be stored in the method area or heap area )

Convert to a java.lang.Class object ;

connect

verification

The verification stage mainly includes four inspection processes : File format validation , Metadata validation , Bytecode verification and symbol reference verification ;

get ready

Allocate memory space for all static variables in a class , And set an initial value for it ( Since no objects have been generated yet , Instance variables will no longer be within the scope of this operation );

analysis

Convert all symbol references in the constant pool to direct references ( Get class or field , Method's pointer or offset in memory , To call the method directly ). This stage can be executed after initialization .

initialization

  In the preparation stage of connection , Class variable has been assigned the initial value required by the system once , In the initialization phase , Class variables and other resources are initialized according to the logic written by the programmer himself , Here's an example :
public static int value1 = 5; public static int value2 = 6; static{ value2 =
66; }
In the preparation stage value1 and value2 All equal to 0;

In the initialization phase value1 and value2 Equal to 5 and 66;

*
All class variable initialization statements and static code blocks are placed in the collector by the front-end compiler at compile time , Store in a special way , This method is <clinit> method , Namely class / Interface initialization method , This method can only be used by JVM call ;
* The order in which the compiler collects is determined by the order in which statements appear in the source file , Only variables defined before a static statement block can be accessed in a static statement block ;
*
If the superclass has not been initialized , Initialization of superclass is preferred , But in <clinit> Method does not display the calling superclass <clinit> method , from JVM Responsible for ensuring a class's <clinit> Before method execution , Its superclass <clinit> Method has been executed .
*
JVM You must ensure that a class is in the process of initialization , If multithreading needs to initialize it at the same time , Only one of the threads can be allowed to perform initialization operations on it , The rest of the threads must wait , Only after the active thread finishes initializing the class , Will notify other threads waiting .( So we can use static inner class to realize thread safety )
* If a class does not declare any class variables , There are no static code blocks , Then there can be no classes <clinit> method ;
When to trigger initialization

* When creating a new object instance for a type ( such as new, reflex , serialize )
* When a static method of type is called ( Execution in bytecode invokestatic instructions )
*
Calling a static field of a type or interface , Or when you assign values to these static fields ( In bytecode , implement getstatic perhaps putstatic instructions ), But with final Except static fields decorated , It is initialized as a compile time constant expression
* call JavaAPI Reflection method in ( For example, call java.lang.Class Method in , perhaps java.lang.reflect Methods of other classes in the package )
* When initializing a derived class of a class (Java When the virtual machine specification explicitly requires a class to be initialized , Its superclass must complete the initialization operation in advance , Interface exception )
* JVM Launch contains main Method's start class time .  
  Custom class loader

  To create a user's own classloader , Just inherit java.lang.ClassLoader class , And then cover its findClass(String
name) Method , That is, how to get the byte stream of the class .

If you want to meet the specification of parent delegation , Then rewrite findClass method ( User defined class loading logic ); To destroy , rewrite loadClass method ( The concrete logic realization of parents' delegation ).

example :
package classloader; import java.io.ByteArrayOutputStream; import java.io.File;
import java.io.FileInputStream; import java.io.IOException; import
java.io.InputStream;class TestClassLoad { @Override public String toString() {
return " Class loaded successfully ."; } } public class PathClassLoader extends ClassLoader { private
String classPath;public PathClassLoader(String classPath) { this.classPath =
classPath; } @Overrideprotected Class<?> findClass(String name) throws
ClassNotFoundException {byte[] classData = getData(name); if (classData == null
) {throw new ClassNotFoundException(); } else { return defineClass(name,
classData, 0, classData.length); } } private byte[] getData(String className) {
String path= classPath + File.separatorChar + className.replace('.',
File.separatorChar) + ".class"; try { InputStream is = new
FileInputStream(path); ByteArrayOutputStream stream= new
ByteArrayOutputStream();byte[] buffer = new byte[2048]; int num = 0; while
((num = is.read(buffer)) != -1) { stream.write(buffer, 0, num); } return
stream.toByteArray(); }catch (IOException e) { e.printStackTrace(); } return
null; } public static void main(String args[]) throws ClassNotFoundException,
InstantiationException, IllegalAccessException { ClassLoader pcl= new
PathClassLoader("D:\\ProgramFiles\\eclipseNew\\workspace\\cp-lib\\bin"); Class c
= pcl.loadClass("classloader.TestClassLoad");// Note include package name
System.out.println(c.newInstance());// Printing class loaded successfully . } }
 

 

JAVA Hot deployment implementation


Let's talk about hot deployment first (hotswap), Hot deployment does not restart Java Under the premise of virtual machine , Automatic detection class Changes in documents , Update runtime class
act .Java Class is passed Java Virtual machine loaded , Of a class class The file is being classloader After loading , The corresponding Class
object , You can then create an instance of this class . The default virtual machine behavior only loads classes at startup , If a class needs to be updated later , Simply replace compiled class file ,Java
Virtual machines do not update running class. If you want to achieve hot deployment , The most fundamental way is to modify the source code of the virtual machine , change classloader Load behavior of , Enable virtual monitoring
class Update of documents , Reload class file , It's destructive , For subsequent JVM There's a big hole in the upgrade .

Another friendly way is to create your own classloader To load what needs listening class, In this way, you can control the timing of class loading , For hot deployment . 

  Hot deployment steps :

1, Destroy custom classloader( Loaded by the loader class It will also be uninstalled automatically );

2, to update class

3, Use new ClassLoader Unload class 

JVM In Class Only the following three conditions are met , To be GC recovery , That is to say Class unmounted (unload):

   - All instances of this class have been GC, that is JVM This does not exist in Class Any instance of .
   - Load the ClassLoader Has been GC.
   - Of this type java.lang.Class Object is not referenced anywhere , If you cannot access a method of this class through reflection anywhere

 

Extend the problem for analysis :


See this topic , A lot of people will think I wrote mine java code , As for class ,JVM Load as you like , Bloggers have long believed the same . With the accumulation of programming experience , Growing sense of the importance of understanding virtual machine related Essentials . Not much gossip , Old rules , Let's start with a code .
public class SSClass { static { System.out.println("SSClass"); } } public class
SuperClassextends SSClass { static { System.out.println("SuperClass init!"); }
public static int value = 123; public SuperClass() { System.out.println("init
SuperClass"); } } public class SubClass extends SuperClass { static {
System.out.println("SubClass init"); } static int a; public SubClass() {
System.out.println("init SubClass"); } } public class NotInitialization { public
static void main(String[] args) { System.out.println(SubClass.value); } }
 
Operation results : SSClass SuperClass init! 123
 

The answer is right ?

Some may wonder : Why there is no output SubClass
init.ok~ Explain : For static fields , Only classes that directly define this field will be initialized , Therefore, the static field defined in the parent class is referenced by its subclass , Only the initialization of the parent class will be triggered, not the initialization of the child class .

This involves the loading mechanism of virtual machine classes . If you are interested , You can keep looking .

 

Class loading process


Class starts from being loaded into virtual machine memory , Until the memory is unloaded , Its entire life cycle includes : load (Loading), verification (Verification), get ready (Preparation), analysis (Resolution), initialization (Initialization), use (Using) And uninstall (Unloading)7 Stages . Preparation , verification , analysis 3 Parts are collectively referred to as connections (Linking). As shown in the figure .




load , verification , get ready , Initialize and uninstall this 5 The sequence of stages is determined , The loading process of a class must start step by step in this order , The parsing phase is not necessarily : It can in some cases start after the initialization phase , This is for support Java Runtime binding for languages ( Also known as dynamic binding or late binding ). The following statements have been made HotSpot As a benchmark .

load

In the loading phase ( You can refer to java.lang.ClassLoader Of loadClass() method ), The virtual machine needs to complete the following 3 One thing :

* Obtain the binary byte stream defining a class by its fully qualified name ( There's no indication that you're going to Class Get in file , From other channels , for example : network , Dynamic generation , Database, etc );
* Convert the static storage structure represented by this byte stream to the runtime data structure of the method area ;
* Generate in memory a java.lang.Class object , As the access to all kinds of data of this class in method area ;

Load phase and connect phase (Linking) Part of ( For example, some byte code file format verification actions ) It's cross cutting , Loading phase not completed , Connection phase may have started , But these actions are carried out in the loading phase , Content still in the connection phase , The starting time of these two stages is still in a fixed order .

verification

Verification is the first step in the connection phase , The purpose of this phase is to ensure Class The information contained in the byte stream of the file meets the requirements of the current virtual machine , And will not endanger the security of virtual machine itself .
The verification phase will be roughly completed 4 Inspection actions in stages :

*
File format validation : Verify that the byte stream matches Class Specification of file format ; for example : Whether to use magic 0xCAFEBABE start , Whether the primary and secondary version numbers are within the processing range of the current virtual machine , Whether constants in constant pool have unsupported types .
*
Metadata validation : Semantic analysis of information described by bytecode ( be careful : contrast javac Semantic analysis in compilation phase ), In order to ensure that the information described in it complies with Java Requirements of language specification ; for example : Does this class have a parent , except java.lang.Object outside .
* Bytecode verification : Through data flow and control flow analysis , Make sure the program semantics are legal , Logical .
* Symbol reference validation : Ensure that parsing actions are performed correctly .

The verification phase is very important , But it's not necessary , It has no effect on the running time of the program , If the referenced class is repeatedly verified , Then consider adopting -Xverifynone Parameter to close most class validation actions , To reduce the load time of virtual machine classes .

get ready


The preparation stage is the stage of formally allocating memory for class variables and setting the initial value of class variables , The memory used by these variables will be allocated in the method area . In this case, only class variables are allocated ( cover static Decorated variable ), Not instance variables , Instance variables are allocated to the heap along with the object when it is instantiated . secondly , The initial value here “ General situation ” Next is the zero value of the data type ,假设一个类变量的定义为:
public static int value=123;

那变量value在准备阶段过后的初始值为0而不是123.因为这时候尚未开始执行任何java方法,而把value赋值为123的putstatic指令是程序被编译后,存放于类构造器()方法之中,所以把value赋值为123的动作将在初始化阶段才会执行.

至于“特殊情况”是指:public static final int
value=123,即当类字段的字段属性是ConstantValue时,会在准备阶段初始化为指定的值,所以标注为final之后,value的值在准备阶段初始化为123而非0.

解析

解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程.解析动作主要针对类或接口,字段,类方法,接口方法,方法类型,方法句柄和调用点限定符7类符号引用进行.

初始化


类初始化阶段是类加载过程的最后一步,到了初始化阶段,才真正开始执行类中定义的java程序代码.在准备极端,变量已经付过一次系统要求的初始值,而在初始化阶段,则根据程序猿通过程序制定的主管计划去初始化类变量和其他资源,或者说:初始化阶段是执行类构造器
<clinit>()方法的过程.

<clinit>()
方法是由编译器自动收集类中的所有类变量的赋值动作和静态语句块static{}中的语句合并产生的,编译器收集的顺序是由语句在源文件中出现的顺序所决定的,静态语句块只能访问到定义在静态语句块之前的变量,定义在它之后的变量,在前面的静态语句块可以赋值,但是不能访问.如下:
public class Test { static { i=0; System.out.println(i);//这句编译器会报错:Cannot
reference a field before it is defined(非法向前应用) } static int i=1; }
那么去掉报错的那句,改成下面:
public class Test { static { i=0; // System.out.println(i); } static int i=1;
public static void main(String args[]) { System.out.println(i); } }

输出结果是什么呢?当然是1啦~在准备阶段我们知道i=0,然后类初始化阶段按照顺序执行,首先执行static块中的i=0,接着执行static赋值操作i=1,最后在main方法中获取i的值为1.

()方法与实例构造器<init>()方法不同,它不需要显示地调用父类构造器,虚拟机会保证在子类<init>()方法执行之前,父类的<clinit>()
方法方法已经执行完毕,回到本文开篇的举例代码中,结果会打印输出:SSClass就是这个道理.

由于父类的<clinit>()方法先执行,也就意味着父类中定义的静态语句块要优先于子类的变量赋值操作.

<clinit>()方法对于类或者接口来说并不是必需的,如果一个类中没有静态语句块,也没有对变量的赋值操作,那么编译器可以不为这个类生产<clinit>()
方法.

接口中不能使用静态语句块,但仍然有变量初始化的赋值操作,因此接口与类一样都会生成<clinit>()方法.但接口与类不同的是,执行接口的<clinit>()
方法不需要先执行父接口的<clinit>()方法.只有当父接口中定义的变量使用时,父接口才会初始化.另外,接口的实现类在初始化时也一样不会执行接口的
<clinit>()方法.

虚拟机会保证一个类的<clinit>()方法在多线程环境中被正确的加锁,同步,如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的
<clinit>()方法,其他线程都需要阻塞等待,直到活动线程执行<clinit>()方法完毕.如果在一个类的<clinit>()
方法中有耗时很长的操作,就可能造成多个线程阻塞,在实际应用中这种阻塞往往是隐藏的.
package jvm.classload; public class DealLoopTest { static class DeadLoopClass {
static { if(true) { System.out.println(Thread.currentThread()+"init
DeadLoopClass"); while(true) { } } } } public static void main(String[] args) {
Runnable script= new Runnable(){ public void run() {
System.out.println(Thread.currentThread()+" start"); DeadLoopClass dlc = new
DeadLoopClass(); System.out.println(Thread.currentThread()+" run over"); } };
Thread thread1= new Thread(script); Thread thread2 = new Thread(script);
thread1.start(); thread2.start(); } }
 
 
运行结果:(即一条线程在死循环以模拟长时间操作,另一条线程在阻塞等待)
Thread[Thread-0,5,main] start Thread[Thread-1,5,main] start Thread[Thread
-0,5,main]init DeadLoopClass
 


需要注意的是,其他线程虽然会被阻塞,但如果执行()方法的那条线程退出()方法后,其他线程唤醒之后不会再次进入()方法.同一个类加载器下,一个类型只会初始化一次.
将上面代码中的静态块替换如下:
static { System.out.println(Thread.currentThread() + "init DeadLoopClass"); try
{ TimeUnit.SECONDS.sleep(10); } catch (InterruptedException e) {
e.printStackTrace(); } }
运行结果:
Thread[Thread-0,5,main] start Thread[Thread-1,5,main] start Thread[Thread-1,5
,main]init DeadLoopClass (之后sleep 10s) Thread[Thread-1,5,main] run over
Thread[Thread-0,5,main] run over
 

虚拟机规范严格规定了有且只有5中情况(jdk1.7)必须对类进行“初始化”(而加载,验证,准备自然需要在此之前开始):

*
遇到new,getstatic,putstatic,invokestatic这失调字节码指令时,如果类没有进行过初始化,则需要先触发其初始化.生成这4条指令的最常见的Java代码场景是:使用new关键字实例化对象的时候,读取或设置一个类的静态字段(被final修饰,已在编译器把结果放入常量池的静态字段除外)的时候,以及调用一个类的静态方法的时候.
* 使用java.lang.reflect包的方法对类进行反射调用的时候,如果类没有进行过初始化,则需要先触发其初始化.
* 当初始化一个类的时候,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化.
* 当虚拟机启动时,用户需要指定一个要执行的主类(包含main()方法的那个类),虚拟机会先初始化这个主类.
*
当使用jdk1.7动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后的解析结果REF_getstatic,REF_putstatic,REF_invokeStatic的方法句柄,并且这个方法句柄所对应的类没有进行初始化,则需要先出触发其初始化.
开篇已经举了一个范例:通过子类引用付了的静态字段,不会导致子类初始化.
这里再举两个例子.

* 通过数组定义来引用类,不会触发此类的初始化:(SuperClass类已在本文开篇定义)
* public class NotInitialization { public static void main(String[] args) {
SuperClass[] sca= new SuperClass[10]; } }
 
 
运行结果:(无)

* 常量在编译阶段会存入调用类的常量池中,本质上并没有直接引用到定义常量的类,因此不会触发定义常量的类的初始化: public class
ConstClass {static { System.out.println("ConstClass init!"); } public static
final String HELLOWORLD = "hello world"; } public class NotInitialization {
public static void main(String[] args) {
System.out.println(ConstClass.HELLOWORLD); } }
 
 
运行结果:
hello world
 

 

 

参考:类加载机制 <http://www.cnblogs.com/chenpi/p/5393650.html>

参考:Java虚拟机类加载机制 <http://www.cnblogs.com/andy-zhou/p/5324698.html>