JVM and the principle of garbage collection mechanism

JVM

Java Virtual Machine The Java virtual machine (JVM) is an essential mechanism for running Java programs. The JVM implements the most important feature of the Java language: platform independence. Principle: The compiled Java program instructions are not directly executed on the CPU of the hardware system, but are executed by the JVM. The JVM shields the information related to the specific platform, so that the Java language compiler only needs to generate the target bytecode (.class) running on the JVM, and it can run on various platforms without modification. When the Java virtual machine executes the bytecode, it interprets the bytecode into machine instructions for execution on a specific platform. So achieve java platform independence. It is a reliable guarantee that Java programs can be seamlessly ported between multiple platforms, and it is also a security check engine for Java programs (and also performs security checks).

The JVM is the interface between the compiled Java program (.class file) and the hardware system (after compilation: javac is the Java language compiler included in the JDK. This tool can compile source files with a suffix of .java into a suffix of .java Bytecode named .class that can run on the Java Virtual Machine.)


JVM architecture:

Image taken from  http://javapapers.com/java/java-garbage-collection-introduction/

JVM = class loader classloader + execution engine + runtime data area runtime data area
classloader loads the class file on the hard disk into the runtime data area in the JVM, but it is not responsible for whether the class file can be executed, and this is The execution engine is responsible.


classloader

Function: Load .class file
classloader There are two ways (timing) to load class:
1. Implicit: During the running process, when an object is generated in the new method, the classLoader is implicitly called to the JVM
2. Explicit: through class.forname () dynamic loading

Parent Delegation Model:

The class loading process adopts the parent delegation mechanism, which can better ensure the security of the Java platform.
This model requires that in addition to the Bootstrap class loader at the top level, the rest of the class loaders should have their own 父类加载器. The child class loader and the parent class loader are 不是以继承(Inheritance)的关系implemented, but 组合(Composition)关系the code of the parent loader is reused. Each class loader has its own namespace (composed of the classes loaded by the loader and all parent class loaders. In the same namespace, the full name of the class (including the package name of the class) will not be the same. two classes; in different namespaces, there may be two classes with the same full name of the class (including the package name of the class)

The working process of the parent delegation model is as follows:

1. The current ClassLoader firstly checks whether the class has been loaded from the class that it has loaded, and if it has been loaded, it directly returns the originally loaded class.

每个类加载器都有自己的加载缓存,当一个类被加载了以后就会放入缓存,
等下次加载的时候就可以直接返回了。

2. When the loaded class is not found in the current classLoader's cache, the parent class loader is entrusted to load it. The parent class loader adopts the same strategy. First, check its own cache, and then entrust the parent class of the parent class to load. To bootstrap ClassLoader.
3. When all parent class loaders are not loaded, the current class loader will load it and put it into its own cache, so that it can return directly next time there is a loading request.

使用这种模型来组织类加载器之间的关系的好处:
主要是为了安全性,避免用户自己编写的类动态替换 Java 的一些核心类,比如 String,同时也避免了重复加载,因为 JVM 中区分不同类,不仅仅是根据类名,相同的 class 文件被不同的 ClassLoader 加载就是不同的两个类,如果相互转型的话会抛java.lang.ClassCaseException.

类加载器 classloader 是具有层次结构的,也就是父子关系。其中,Bootstrap 是所有类加载器的父亲。如下图所示:
Bootstrap class loader: 父类
当运行 java 虚拟机时,这个类加载器被创建,它负责加载虚拟机的核心类库,如 java.lang.* 等。例如 java.lang.Object 就是由根类加载器加载的。需要注意的是,这个类加载器不是用 java 语言写的,而是用 C/C++ 写的。
Extension class loader:
这个加载器加载出了基本 API 之外的一些拓展类。
AppClass Loader:
加载应用程序和程序员自定义的类。

除了以上虚拟机自带的加载器以外,用户还可以定制自己的类加载器(User-defined Class Loader)。Java 提供了抽象类 java.lang.ClassLoader,所有用户自定义的类加载器应该继承 ClassLoader 类。

这是JVM分工自治生态系统的一个很好的体现。

http://www.importnew.com/6581.html


执行引擎

作用: 执行字节码,或者执行本地方法

runtime data area

JVM 运行时数据区 (JVM Runtime Area) 其实就是指 JVM 在运行期间,其对JVM内存空间的划分和分配。JVM在运行时将数据划分为了6个区域来存储。

程序员写的所有程序都被加载到运行时数据区域中,不同类别存放在heap, java stack, native method stack, PC register, method area.

下面对各个部分的功能和存储的内容进行描述:

1、PC程序计数器:一块较小的内存空间,可以看做是当前线程所执行的字节码的行号指示器, NAMELY存储每个线程下一步将执行的JVM指令,如该方法为native的,则PC寄存器中不存储任何信息。Java 的多线程机制离不开程序计数器,每个线程都有一个自己的PC,以便完成不同线程上下文环境的切换。

2、java虚拟机栈:与 PC 一样,java 虚拟机栈也是线程私有的。每一个 JVM 线程都有自己的 java 虚拟机栈,这个栈与线程同时创建,它的生命周期与线程相同。虚拟机栈描述的是Java 方法执行的内存模型:每个方法被执行的时候都会同时创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法被调用直至执行完成的过程就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程

3、本地方法栈:与虚拟机栈的作用相似,虚拟机栈为虚拟机执行执行java方法服务,而本地方法栈则为虚拟机使用到的本地方法服务。

4、Java堆:被所有线程共享的一块存储区域,在虚拟机启动时创建,它是JVM用来存储对象实例以及数组值的区域,可以认为Java中所有通过new创建的对象的内存都在此分配。

Java堆在JVM启动的时候就被创建,堆中储存了各种对象,这些对象被自动管理内存系统(Automatic Storage Management System,也即是常说的 “Garbage Collector(垃圾回收器)”)所管理。这些对象无需、也无法显示地被销毁。

JVM将Heap分为两块:新生代New Generation和旧生代Old Generation

Note:

  • 堆在JVM是所有线程共享的,因此在其上进行对象内存的分配均需要进行加锁,这也是new开销比较大的原因。
  • 鉴于上面的原因,Sun Hotspot JVM为了提升对象内存分配的效率,对于所创建的线程都会分配一块独立的空间,这块空间又称为TLAB
  • TLAB仅作用于新生代的Eden Space,因此在编写Java程序时,通常多个小的对象比大的对象分配起来更加高效

5、方法区
方法区和堆区域一样,是各个线程共享的内存区域,它用于存储每一个类的结构信息,例如运行时常量池,成员变量和方法数据,构造函数和普通函数的字节码内容,还包括一些在类、实例、接口初始化时用到的特殊方法。当开发人员在程序中通过Class对象中的getName、isInstance等方法获取信息时,这些数据都来自方法区。

方法区也是全局共享的,在虚拟机启动时候创建。在一定条件下它也会被GC。这块区域对应Permanent Generation 持久代。 XX:PermSize指定大小。

6、运行时常量池
其空间从方法区中分配,存放的为类中固定的常量信息、方法和域的引用信息。


GC

javapapers
Java garbage collection is an automatic process to manage the runtime memory used by programs. By doing it automatic JVM relieves the programmer of the overhead of assigning and freeing up memory resources in a program.
java 与 C语言相比的一个优势是,可以通过自己的JVM自动分配和回收内存空间。

何为GC?
垃圾回收机制是由垃圾收集器Garbage Collection GC来实现的,GC是后台的守护进程。它的特别之处是它是一个低优先级进程,但是可以根据内存的使用情况动态的调整他的优先级。因此,它是在内存中低到一定限度时才会自动运行,从而实现对内存的回收。这就是垃圾回收的时间不确定的原因。

为何要这样设计:因为GC也是进程,也要消耗CPU等资源,如果GC执行过于频繁会对java的程序的执行产生较大的影响(java解释器本来就不快),因此JVM的设计者们选着了不定期的gc。

GC有关的是: runtime data area 中的 heap(对象实例会存储在这里) 和 gabage collector方法。
程序运行期间,所有对象实例存储在运行时数据区域的heap中,当一个对象不再被引用(使用),它就需要被收回。在GC过程中,这些不再被使用的对象从heap中收回,这样就会有空间被循环利用。
GC为内存中不再使用的对象进行回收,GC中调用回收的方法--收集器garbage collector. 由于GC要消耗一些资源和时间,Java 在对对象的生命周期特征(eden or survivor)进行分析之后,采用了分代的方式进行对象的收集,以缩短GC对应用造成的暂停。

在垃圾回收器回收内存之前,还需要一些清理工作。
因为垃圾回收gc只能回收通过new关键字申请的内存(在堆上),但是堆上的内存并不完全是通过new申请分配的。还有一些本地方法(一般是调用的C方法)。这部分“特殊的内存”如果不手动释放,就会导致内存泄露,gc是无法回收这部分内存的。
所以需要在finalize中用本地方法(native method)如free操作等,再使用gc方法。显示的GC方法是system.gc()

垃圾回收技术

方法一:引用计数法。简单但速度很慢。缺陷是:不能处理循环引用的情况。
方法二:停止-复制(stop and copy)。效率低,需要的空间大,优点,不会产生碎片。
方法三:标记 - 清除算法 (mark and sweep)。速度较快,占用空间少,标记清除后会产生大量的碎片。

JAVA虚拟机中是如何做的?
java的做法很聪明,我们称之为"自适应"的垃圾回收器,或者是"自适应的、分代的、停止-复制、标记-清扫"式垃圾回收器。它会根据不同的环境和需要选择不同的处理方式。

heap组成

由于GC需要消耗一些资源和时间的,Java在对对象的生命周期特征进行分析后,采用了分代的方式来进行对象的收集,即按照新生代、旧生代的方式来对对象进行收集,以尽可能的缩短GC对应用造成的暂停.
heap 的组成有三区域/世代:(可以理解随着时间,对象实例不断变换heap中的等级,有点像年级)

  1. 新生代 Young Generation

    1. Eden Space 任何新进入运行时数据区域的实例都会存放在此
    2. S0 Suvivor Space 存在时间较长,经过垃圾回收没有被清除的实例,就从Eden 搬到了S0
    3. S1 Survivor Space 同理,存在时间更长的实例,就从S0 搬到了S1
  2. 旧生代 Old Generation/tenured
    同理,存在时间更长的实例,对象多次回收没被清除,就从S1 搬到了tenured

  3. Perm 存放运行时数据区的方法区

Java 不同的世代使用不同的 GC 算法。

  1. Minor collection:
    新生代 Young Generation 使用将 Eden 还有 Survivor 内的数据利用 semi-space 做复制收集(Copying collection), 并将原本 Survivor 内经过多次垃圾收集仍然存活的对象移动到 Tenured。
  2. Major collection 则会进行 Minor collection,Tenured 世代则进行标记压缩收集。


To note that:
这个搬运工作都是GC 完成的,这也是garbage collector 的名字来源,而不是叫garbage cleaner. GC负责在heap中搬运实例,以及收回存储空间。

GC工作原理

JVM 分别对新生代和旧生代采用不同的垃圾回收机制

何为垃圾?

Java中那些不可达的对象就会变成垃圾。那么什么叫做不可达?其实就是没有办法再引用到该对象了。主要有以下情况使对象变为垃圾:
1.对非线程的对象来说,所有的活动线程都不能访问该对象,那么该对象就会变为垃圾。
2.对线程对象来说,满足上面的条件,且线程未启动或者已停止。

例如: 
(1)改变对象的引用,如置为null或者指向其他对象。 
   Object x=new Object();//object1 Object y=new Object();//object2 x=y;//object1 变为垃圾 x=y=null;//object2 变为垃圾 (2)超出作用域 if(i==0){ Object x=new Object();//object1 }//括号结束后object1将无法被引用,变为垃圾 (3)类嵌套导致未完全释放 class A{ A a; } A x= new A();//分配一个空间 x.a= new A();//又分配了一个空间 x=null;//将会产生两个垃圾 (4)线程中的垃圾 class A implements Runnable{ void run(){ //.... } } //main A x=new A();//object1 x.start(); x=null;//等线程执行完后object1才被认定为垃圾 这样看,确实在代码执行过程中会产生很多垃圾,不过不用担心,java可以有效地处理他们。 

JVM中将对象的引用分为了四种类型,不同的对象引用类型会造成GC采用不同的方法进行回收:
(1)强引用:默认情况下,对象采用的均为强引用
(GC不会回收)
(2)软引用:软引用是Java中提供的一种比较适合于缓存场景的应用
(只有在内存不够用的情况下才会被GC)
(3)弱引用:在GC时一定会被GC回收
(4)虚引用:在GC时一定会被GC回收

载请注明出处http://segmentfault.com/blog/exploring/

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325343634&siteId=291194637