JVM 垃圾回收

来源:互联网 发布:mac如何快速回到桌面 编辑:程序博客网 时间:2024/06/07 00:48

由于java时运行在虚拟机上的,因此与C、C++等语言不同,java可以自己进行内存垃圾的回收,不需要程序员自己处理对象的销毁。

但是掌握GC对于我们对jvm调优有很大的帮助。

对于jvm的内存模型我们可以看到:


按照线程共享的方式可以分为:

  • 线程共享:堆,方法区
  • 非线程共享:程序计数器,JVM栈,本地方法栈

解释下各部分的含义:

  1. 方法区:主要存储JVM加载的类信息,静态变量,常量,即时编译器编译的代码(这里涉及到即时编译器JIT相关内容,以后再做介绍)
  2. 堆:主要存储生成的对象信息,即对象的实例。
  3. 程序计数器:程序执行的顺序,当前执行的字节码的行号。当java文件被编译成class以后会形成一组以8位字节为基础单位的二进制流。程序运行到哪一步就是根据改变计数器 来实现的。最明显的使用就是,当采用多线程的时候,单一处理器只能在一个时间点运行一个线程,而当多个线程抢占资源或者共享资源的时候,需要记录每个线程的程序执行到什么位置,因此程序计数器是线程级的,各线程之间的计数器互不影响。
  4. JVM栈:主要存储局部变量,操作数栈,动态链接,方法出口信息等。其中比较重要的就是局部变量表这块,其中不光包括一些基本的数据类型,如int,long等 还包括一些对象的引用,如Integer等对象的地址。
  5. 本地方法栈:为虚拟机使用到的native方法,使用native修饰的java方法只有声明,他的实现需要用到其他语言。native关键字说明其修饰的方法是一个原生态方法,方法对应的实现不是在当前文件,而是在用其他语言(如C和C++)实现的文件中

比如现在有一个类

package dto;import java.io.Serializable;public class StudentDto implements Serializable {/** *  */private static final long serialVersionUID = 3676795379790467770L;private int id;private String name;public int getId() {return id;}public void setId(int id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}}

当通过StudentDto studentDto = newStudentDto();创建对象的时候,内存会为做如下的操作


JVM栈中主要存储的是对象的引用,堆中存放的是对象的数据。因此在内存回收的时候,主要针对的是堆内存的回收。

根据分代原理将内存划分为年轻代,年老代和持久代,其中持久代主要存储的是编译的类,方法,常量池,字段和方法数据,构造函数和普通方法的字节码内容以及类、实例、接口初始化时需要使用到的特殊方法等数据。

具体分代如下图


根据复制算法:

其中一个Eden区,两个 Survivor区(from区、to区)。大部分对象在Eden区中生成。当Eden区满时,还存活的对象将被复制到from区,当这个区满时,执行normal gc,此区的存活对象将被复制到to区,当这个区也满了的时候,执行full gc,从第一个from区复制过来的并且此时还存活的对象,将被复制到“年老区"。

那么我们如何判断对象是否需要被移除内存呢?

目前有两种算法,一种是引用计数器算法,还有一种是可达性分析算法

先来说说引用计数器算法:当对象被引用的时候会在对象计数器上+1,当引用失效的时候计数器-1。但是随之而来就出现了问题,如果两个对象互相引用,虽然两个对象都为null,但是不会被收回。

        class TestA{  public TestB b;}class TestB{  public TestA a;}public class Main{    public static void main(String[] args){        A a = new A();        B b = new B();        a.b=b;        b.a=a;        a = null;        b = null;    }}
因此现在的java垃圾回收算法多用可达性分析算法。这里引用一张书上的图片


object1是“GC ROOT”的对象作为起始点,因此object2,object3,object4都是可达的。而5,6,7是不可达的,因为从object1向下搜索形成的引用链不可达。因此5,6,7会被判断为可回收的对象。而可作为GC ROOT的可以是:

1.虚拟机栈(栈帧中的本地变量表)中引用的对象。

2.方法区中类静态属性引用的对象。

3.方法区中常量引用的对象。

4.本地方法栈中JNI引用的对象。


 


0 0