jvm内存分配和回收策略

在上一篇中,已经介绍了内存结构是什么样的. 这篇来介绍一下 内存是怎么分配的,和怎么回收的.(基本取自《深入理解Java虚拟机》一书)

java技术体系中所提倡的自动内存管理最终可以归结为自动化的解决了两个问题--给对象分配内存以及回收分配给对象的内存.我们下面就来介绍这些是怎么自动化完成的.

下面继续提出问题:

1.怎么判断对象是否能被回收?

2.垃圾回收是怎么进行的?

3.方法区存在垃圾回收吗?

4.垃圾回收都有什么算法?

5.MinorGC,FullGC,新生代,老年代,永久代,Eden,Survivor 这些名词都是什么意思?

6.内存分配策略(规则)是什么?

下面来解决问题:

1.怎么判断对象是否能被回收?

java是采用的跟搜索算法(GC Roots Tracing)来判定对象是否存活.简单讲就是如果一个对象到 根节点之间不可达时,就可以被回收了.

那么问题来了.根节点是什么呢? 在java语言里,能做根节点的包括下面几种:

1) 虚拟机栈中的引用对象

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

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

4)本地方法栈JNI(即Native方法)的引用的对象.

这里需要说明一下,上面所说的引用,都是指的强引用. 我们知道java中引用有四种.强,软,弱,虚.我们平常的引用都是指的强引用,像缓存一般使用软引用来实现.

2.垃圾回收是怎么进行的?

上面一点讲了怎么判断一个对象是否能被回收,那么是否能被回收,就一定会被回收呢? 当然不是.

首先垃圾回收行为是不确定什么时候执行的. 其次.回收一个对象,至少要经理两次标记过程:

1)判断对象是否与GC Roots之间有引用链

2)如果没有,则进行第一次标记,并按此对象是否需要执行finalize()方法进行筛选.

3)如果对象不需要执行finalize()方法,则第二次标记为可回收.此时真正可回收.

4)如果需要执行finalize()方法,则会被放在一个F-Queue队列中去执行,稍后GC将对F-Queue中的对象进行二次标记.如果成功拯救自己(譬如把自己赋值给某个引用链上的对象),则会在标记中移除. 如果没能拯救,则此时真正可回收.

另外说下.《深入理解Java虚拟机》一书建议我们忘掉 finalize()这个方法.

3.方法区存在垃圾回收吗?

方法区在HotSpot虚拟机中又名永久代, 永久代还存在或者说还需要垃圾回收吗?

这个区域是存在垃圾回收的,但是垃圾回收的效率比较低.就是说回收一次释放不了多少空间.永久代主要回收内容是 废弃常量和无用的类.

在大量使用反射,动态代理,CGLib等bytecode框架的场景,以及动态生成JSP和OSGi这类频繁自定义ClassLoader的场景都需要虚拟机具备回收无用的类的功能.以保障永久代不溢出.

4.垃圾回收都有什么算法?

常用的算法 标记-清除算法,复制算法,标记-整理算法. 分代收集算法.

前面3种算法,各有优缺点,分代收集算法其实只是把java堆分成了几块,每块使用前面算法中最适合的算法.

垃圾回收器也有很多种.具体可以看《深入理解Java虚拟机》一书

5.新生代,老年代,永久代,Eden,Survivor ,MinorGC,FullGC,这些名词都是什么意思?

让我们打开jdk/bin/jvisualvm.exe  这个工具,装上Visual GC 这个插件.打开后如下图,是我eclipse运行时的截图.

整个jvm内存情况在图中都有显示,

Perm 就是方法区,也称为永久代.

Old+Eden+S0+S1 = 堆. Eden+S0+S1 = Young

Old 就是老年代, Young就是新生代.

S0和S1中一定会有一个为空,这是因为垃圾回收算法决定的(复制算法)HotSpot默认的S0,S1与Eden的大小比例为1:8.

Eden区满后,会触发MinorGC(新生代GC),Old区满后会触发FullGC(老年代GC,Major GC). (注意不一定是存满才触发.)

新生代GC比较频繁,但是回收速度也快,老年代GC理论上不会太频繁,但是回收速度比较慢.(对应图中,GC time 21次,1.251s,全部都是Eden区发生的,OldGen区没有发生FullGC)

为什么新生代还要细分为Eden,Survivor0,Survivor1呢? 其实这都是为了配合垃圾回收算法来分的.

6.java内存分配策略是什么?

其实从上面的对堆的划分,就大概能了解到java内存是怎么分的了.

简单讲就是优先分配在Eden区, 如果经历了一些回收后,还没死掉,那么就有资格进入到Old区. 又或者有些比较大的对象,直接就分配在Old区.

上面说的是堆的内存分配, 栈的内存分配就更简单了.java栈的分配是和线程绑定在一起的,创建一个线程时,就会为这个线程创建一个栈,

从栈和堆的功能和作用来比较,堆主要用来存放对象,栈主要用来执行程序.

垃圾回收和分配的说完了, 很多细的东西都没说,因为现阶段感觉还用不到,也不容易理解.

原文地址:https://www.cnblogs.com/clovejava/p/7565592.html