Java GC

2018-04-18  本文已影响23人  Scofeild

java 内存区域划分

方法区:

1 主要储存被虚拟机加载的类的信息,常量,静态变量,以及及时编辑器编译后的代码等数据

2 * 被线程共享

3 方法区里有一个 运行常量池 , 大部分用于存放静态编译产生的字面量和符号引用,运行时的常量也可能放在这个常量池里。

虚拟机栈:

1 为Java 方法服务,方法在执行的时候会创建一个栈帧,用于存储 局部变量表,操作数栈,动态链接与方法出口灯,

2 栈为线程私有,生命周期与线程相同。

3. 局部变量表里存储的是基本数据类型、returnAddress类型(指向一条字节码指令的地址)和对象引用,这个对象引用有可能是指向对象起始地址的一

  个指针,也有可能是代表对象的句柄或者与对象相关联的位置。局部变量所需的内存空间在编译器间确定

4.操作数栈的作用主要用来存储运算结果以及运算的操作数,它不同于局部变量表通过索引来访问,而是压栈和出栈的方式

5.每个栈帧都包含一个指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的动态连接.动态链接就是将常量池中的符

号引用在运行期转化为直接引用。

本地方法栈:跟虚拟机栈差不多,主要是为Native方法服务。

程序计数器:唯一一个不发生GC的地方 改变计数值执行字节码命令,分支,循环,跳转,异常处理

,线程恢复等,我感觉跟引用计数法有些像。

堆:对象的创建,一个回收常在这里发生。

判断GC对象

1 引用计数法

引用计数法就是给对象设置一个计数器 每当有一个地方引用这个对象 就将计数器加一,引用失效时,计数器就减一。

问题:循环引用,造成对象回收不掉。

2 可达性分析

从一个Gc Root 对象向下搜索,如果一个对象到GcRoot没有任何引用链相连,则说明此对象不可用。

GcRoot 对象

栈中的对象(基本)

方法区类静态属性引用的对象

方法区常量池引用的对象。

本地方法栈引用的对象

理解上面的 四个 需要对内存区域进行了解以及熟知。

满足上述条件后不一定会回收,还要经历两次标记的过程。

第一次标记 是否有必要执行 finealize()方法,当对象没有覆盖finealize()方法或者已经被虚拟机调用过,那么就认为没有必要,

如果有必要执行 finealize()方法,那么会将这个对象放在 F-Queue的队列中,虚拟机触发一个线程去执行,优先级底的线程,

此时Gc会对此对象进行第二次标记,在第二次标记之前,如果这个对象有新的引用,该对象被移除‘即将回收‘队列,如果没有,则等待回收。

Java 垃圾回收算法

1 标记-清除

标记回收的对象,然后统一回收。

优点:简单

缺点:

1 效率低,标记跟清除都效率低

2 导致大量的不连续的内存碎片,导致程序分配较大的对象,没有充足的内存提前进行一次GC(内存抖动)

2 复制算法

将内存分为两个相等的两部分,每次使用一块,当要使用完了,将还存活的对象复制到第二块内存板上,

然后清除第一块内存,然后在把存活的对象复制回来。(缺点:会浪费内存)

改进:

内存区域进行了重新划分, 8:1:1

较大的那块 叫 Eden区,剩下叫 Survior区

每次优先使用Eden区,弱Eden区满,讲对象复制到第二块内存,然后清除Eden区

如果存活的对象太多,以至于Survior不够,会将这些对象通过分配担保机制

复制到老年代中(Java 堆)。

3 标记-整理

区分 标记-清除,为了解决标记—清除 产生大量内存块碎片的问题。

当对象存活率较高的时候,也解决了复制算法的效率问题。

前两个算法的合集吧 在清除无用对象的时候讲对象移到另一端,然后清除掉边界以外的对象,这样就不会产

零碎的内存碎片了。

4 分代回收

根据对象的生命周期,将堆分为新生代跟老年代,(永久代),新生代对象生命周期存活的短(感觉这块是标记)

每次都会有大量的对象死去,采用复制算法,老年代对象存活时间长,所以可以考虑 标记-清除,或者标记—整理。

上一篇下一篇

猜你喜欢

热点阅读