您的位置:首页 > 其它

Jvm内存区以及垃圾回收算法

2017-03-12 10:32 429 查看
Java虚拟机运行时数据区域被分为五个区域:堆(Heap)、栈(Stack)、本地方法栈(Native Stack)、方法区(Method Area)、程序计数器(Program Count Register)

堆
Java Heap是Java虚拟机管理的内存的最大一块,这块区域随着虚拟机的启动而创建。在实际的运用中,我们创建的对象和数组就是存放在堆里面。
如果你听说线程安全的问题,就会很明确的知道Java Heap是一块共享的区域,操作共享区域的成员就有了锁和同步。
与Java Heap相关的还有Java的垃圾回收机制(GC),Java Heap是垃圾回收器管理的主要区域。程序猿所熟悉的新生代、老生代、永久代的概念就
是在堆里面,现在大多数的GC基本都采用了分代收集算法。如果再细致一点,Java Heap还有Eden空间,From Survivor空间,To Survivor空间等。

栈(Stack)
Java Stack是线程私有的,她的生命周期与线程相同。Java Stack描述的是Java方法执行时的内存模型,每个方法执行时都会创建一个栈帧(Stack Frame)
用语存储局部变量表、操作数栈、动态链接、方法出口

本地方法栈
本地方法栈(Native Stack)与Java虚拟机站(Java Stack)所发挥的作用非常相似,他们之间的区别在于虚拟机栈为虚拟机栈执行java方法(也就是字节码)
服务,而本地方法栈则为使用到Native方法服务

方法区:很多人都更愿意把方法区称为“永久
方法区(Method Area)与堆(Java Heap)一样,是各个线程共享的内存区域,它用于存储虚拟机加载的类信息,常量,静态变量,即时编译器编译后的代码等数据

线程私有的数据区域有:
Java虚拟机栈(Java Stack)
本地方法栈(Native Stack)

线程共有的数据区域有:
堆(Java Heap)
方法区

堆被划分成两个不同的区域:新生代 ( Young )、老年代 ( Old )。新生代 ( Young ) 又被划分为三个区域:Eden、From Survivor、To Survivor。
堆的大小可以通过参数 –Xms、-Xmx 来指定。

新生代 ( Young ) 与老年代 ( Old ) 的比例的值为 1:2( 该值可以通过参数 –XX:NewRatio 来指定 ) 新生代 ( Young ) = 1/3 的堆空间大小。
老年代 ( Old ) = 2/3 的堆空间大小。
新生代 ( Young ) 被细分为 Eden 和 两个 Survivor 区域,这两个 Survivor 区域分别被命名为 from 和 to,默认的Edem : from : to = 8 : 1 : 1

JVM 每次只会使用 Eden 和其中的一块 Survivor 区域来为对象服务,所以无论什么时候,总是有一块 Survivor 区域是空闲着的。

GC 分为两种:Minor GC、Full GC ( 或称为 Major GC )。
1.Minor GC 是发生在新生代中的垃圾收集动作,所采用的是复制算法。
a、新生代几乎是所有 Java 对象出生的地方,即 Java 对象申请的内存以及存放都是在这个地方。
b、Java 中的大部分对象通常不需长久存活,具有朝生夕灭的性质。
c、当一个对象被判定为 “死亡” 的时候,GC 就有责任来回收掉这部分对象的内存空间。
d、新生代是 GC 收集垃圾的频繁区域。
e、当对象在 Eden(+from) 出生后,在经过一次 Minor GC 后,如果对象还存活,并且能够被另外一块 Survivor 区域
所容纳( 这里应为 to 区域 ),则使用复制算法将这些仍然还存活的对象复制到另外一块 Survivor 区域 ( 即 to 区域 ) 中,
f、 然后清理所使用过的 Eden 以及 Survivor 区域 ( 即 from 区域 ),并且将这些对象的年龄设置为1,
g、以后对象在 Survivor 区每熬过一次 Minor GC,就将对象的年龄 + 1,
h、当对象的年龄达到某个值时 ( 默认是 15 岁,可以通过参数 -XX:MaxTenuringThreshold 来设定 ),这些对象就会成为
老年代。
j、但这也不是一定的,对于一些较大的对象 ( 即需要分配一块较大的连续内存空间 ) 则是直接进入到老年代。

复制算法
1、将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。
2、当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。

2.Full GC 是发生在老年代的垃圾收集动作,所采用的是标记-清除算法。
a、老年代里面的对象几乎个个都是在 Survivor 区域中熬过来的,它们是不会那么容易就 “死掉” 了的。
b、Full GC 发生的次数不会有 Minor GC 那么频繁,并且做一次 Full GC 要比进行一次 Minor GC 的时间更长。
c、标记-清除算法收集垃圾的时候会产生许多的内存碎片 ( 即不连续的内存空间 ),
d、此后需要为较大的对象分配内存空间时,若无法找到足够的连续的内存空间,就会提前触发一次 GC 的收集动作。

1、标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象
2、在标记完成后统一回收所有被标记的对象
内存分配与回收策略
下面是使用Serial/Serial Old收集器下(ParNew/Serial Old收集器组合的规则也基本一致)的内存分配和回收的策略

对象优先在Eden分配
1、大多数情况下,对象在新生代Eden区中分配。当Eden区没有足够空间进行分配时,虚拟机将发起一次MinorGC。
2、执行GC后,将存活的对象分配到Survivor空间
3、无法放到Survivor空间的对象,分配到老年代
4、分配到Survivor的对象,经过15次Minor GC后,进入老年代

新生代GC(Minor GC):指发生在新生代的垃圾收集动作,因为Java对象大多都具备朝生夕灭的特性,所以Minor GC非常频繁,一般回收速度也比较快。
老年代GC(Major GC/Full GC):指发生在老年代的GC,出现了Major GC,经常会伴随至少一次的MinorGC(但非绝对的,在Parallel Scavenge收集器的
收集策略里就有直接进行Major GC的策略选择过程)。Major GC的速度一般会比Minor GC慢10倍以上

大对象直接进入老年代
1、大对象:需要大量连续内存空间的Java对象,最典型的大对象就是那种很长的字符串以及数组
2、虚拟机提供了一个-XX:PretenureSizeThreshold参数,令大于这个设置值的对象直接在老年代分配

长期存活的对象将进入老年代
1、虚拟机给每个对象定义了一个对象年龄(Age)计数器
2、如果对象在Eden出生并经过第一次Minor GC后仍然存活,并且能被Survivor容纳的话,将被移动到Survivor空间中,并且对象年龄设为1。
3、对象在Survivor区中每“熬过”一次Minor GC,年龄就增加1岁
4、当它的年龄增加到一定程度(默认为15岁),就将会被晋升到老年代中
5、对象晋升老年代的年龄阈值,可以通过参数-XX:MaxTenuringThreshold设置

动态对象年龄判定
1、为了能更好地适应不同程序的内存状况,虚拟机并不是永远地要求对象的年龄必须达到了MaxTenuringThreshold才能晋升老年代
2、如果在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代,无须等到MaxTenuringThreshold中要求的年龄
4. 并行垃圾回收收集器和CMS收集器区别


CMS(Concurrent Mark-Sweep)是以牺牲吞吐量为代价来获得最短回收停顿时间的垃圾回收器,如果你的应用程序对停顿比较敏感,并且在应用程序运行的时候可以提供更大的内存和更多的CPU(也就是硬件牛逼),那么使用CMS来收集会给你带来好处。还有,如果在JVM中,有相对较多存活时间较长的对象(老年代比较大)会更适合使用CMS。

G1为了能够尽量的做到准实时的响应,例如估算暂停时间的算法、对于经常被引用的对象的特殊处理等,G1为了能够让GC既能够充分的回收内存,又能够尽量少的导致应用的暂停,可谓费尽心思,从G1的论文中的性能评测来看效果也是不错的,不过如果G1能允许开发人员在编写代码时指定哪些对象是不用mark的就更完美了,这对于有巨大缓存的应用而言,会有很大的帮助,G1将随JDK 6 Update 14 beta发布。

并行收集:并行收集使用多线程处理垃圾回收工作,因而速度快,效率高。而且理论上CPU数目越多,越能体现出并行收集器的优势。(串型收集的并发版本,需要暂停jvm) 并行paralise指的是多个任务在多个cpu中一起并行执行,最后将结果合并。效率是N倍。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: