Java内存管理和垃圾回收
笔记,深入理解java虚拟机
Java运行时内存区域
程序计数器,线程独占,当前线程所执行的字节码的行号指示器,每个线程需要记录下执行到哪儿了,下次调度的时候可以继续执行,这个区是唯一不会发生oom的
栈,线程独占,包含虚拟机栈或native method stack,用于存放局部变量的
堆,线程共享,用于分布对象实例的,后面说的内存管理和垃圾回收基本都是针对堆的
方法区,线程共享,用于存放被虚拟机加载的类,常量,静态变量; Java虚拟机规范,把方法区描述为堆的逻辑部分,所以也被称为“永久代”,在大量使用反射,动态代理,ClassLoader的场景下,要考虑永久代的回收
对于一个简单的,对象生成
Object obj = new Object();
会涉及到3个区,
图中描述两种对象访问方式,有句柄,可以屏蔽对象实际地址的改变(gc时对象地址经常会改变),不用句柄效率更高
垃圾回收
垃圾判定
对于如何判定对象是垃圾,教科书的答案是引用计数,并且也在COM,python中得到较好的应用
引用计数的问题是,难以解决循环引用的问题,
A.instance = B; B.instance =A
这样会导致A,B的引用计数都不为0
所以对于Java,C#,Lisp都是采用GC Roots Tracing的方式,
原理也很简单,就是选取一系列GC Roots对象,只保留从root对象可达的对象,其余都是垃圾
Java中的GC roots包含,
- JavaStack中的引用的对象
- 方法区中静态引用指向的对象
- 方法区中常量引用指向的对象
- Native方法中JNI引用的对象
垃圾回收算法
Mark-Sweep,最原始的想法,先标记出所有垃圾对象,然后回收掉;问题是,效率低,而且会产生大量内存碎片
Copying,最简单的copying,把内存分两半,先用一半,然后回收时,把有效对象copy到另外一半
这个首先只适用于年轻代,即垃圾对象占较高比例的case
再者,空间太浪费了,只能用一半
所以,现在实际使用的版本为,分为一个较大的eden区,两个较小的survivor区
比例一般为8:1:1,其中一个survivor区是空的,这样只浪费10%的空间
能这样做的前提就是,每次只有最多10%的对象存活
对象先放到eden区,eden区满了,进行minor gc,把eden区和有数据的survivor区的存活对象,放到另一个空的survivor区中
两个survivor区,虽然命名为from和to,但是其实没有任何区别,完全对等的
当如果survivor区的空间不够,就需要放到年老代(称为handle promotion,即年老代要为survivor提供担保,你那空间不够,可以放我这,类似贷款担保),如果年老代的空间也不够或不能接受Handle promotion失败,就需要full gc去回收年老代
Mark-Compact,前面说copying只适用于存活对象比例较低的case,所以适合年轻代,但对于年老代这样的,用copying肯定是不行的;
Mark-compact,mark还是一样的,只是在回收时,会把对象做平移,消除碎片
垃圾收集器
Serial收集器
最简单的,单线程,收集时会stop the world,所有用户线程暂停
可以用于收集新生代或老年代
收集新生代,用copying算法
收集老年代,用标记-整理,称为serial old
简单高效,适用于单CPU环境;是虚拟机Client模式的默认收集器
缺点,会stop the world
ParNew收集器
只是serial的多线程版本,适用于多核环境,如果在单核的机器上,效率还不如serial
优势是,可以配合CMS收集器使用,因为CMS作为老年代的收集器,只能配合Serial或ParNew作为新生代的收集器
CMS(Concurrent Mark Sweep)收集器
以最短停顿时间为目标的收集器,适合用于网站或B/S系统的服务端,重视服务响应速度的场景。
该收集器相对比较复杂,整个过程分为,
1. 初始标记(CMS initial mark),stop the world,标记GC Roots直接关联到的对象,速度很快
2. 并发标记(CMS concurrenr mark), 并行进行GC Roots Tracing的过程
3. 重新标记(CMS remark),stop the world,由于并发标记和用户线程是并发执行的,所以需要对标记进行最后的修正,消耗时间会大大小于并发标记时间
4. 并发清除(CMS concurrent sweep),最后进行sweep
缺点,
a. CPU敏感,频繁GC会导致CPU卡死
b. 无法处理浮动垃圾(floating garbage),在并发清理阶段产生的新垃圾无法在这次完成回收
c. 需要预留较大的内存,由于CMS收集过程是和用户应用并发进行的,所以不能等到老年代快被占满再做,需要提前进行收集,默认是设为68%,这是比较保守的设定,可以减少以降低gc的次数;但是如果在CMS收集过程中,出现用户应用程序内存不够的情况,会发生”Concurrent Mode Failure”,这样虚拟机只能用后备方案,serial old收集器进行年老代的收集(因为应用已经无法并发执行),这样应用停顿时间就会很长,所以需要设置合理的比例
d. 因为采用sweep,会有大量碎片
Parallel Scavenge收集器
新生代收集器,设计目的是达到可控制的throughput,CPU运行用户代码时间/CPU总消耗时间,说白了,就是保证CPU更多的执行用户代码而非gc
适合后台运算,不太需要交互的场景
但鱼和熊掌不可兼得,throughput和GC停顿时间是需要tradeoff的
降低新生代的空间大小,缩小gc的间隔,都可以减少GC的停顿时间,但也会降低throughput
并且该收集器,支持UseAdaptiveSizePolicy参数, 这样不需要使用者指定新生代大小,Eden和Survivor比例,年老代晋升年龄等,虚拟机会根据运行性能监控去优化调整
Parallel Old收集器
前面提到的parallel sacvenge收集器是无法与CMS收集器配合使用的,所以之前只能配合Serial Old来收集老年代,导致效率低
Parallel Old作为Serial Old的多线程版本,可以更好的和parallel sacvenge收集器配合,真正达到throughput优先的收集
G1(Garbage First)收集器
新一代的收集器,不同于之前的收集器,会收集整个新生代或老年代
G1会把整个Java堆划分为多个固定大小的区域,并跟踪垃圾堆积程度,并每次收集垃圾最多的区域,可以大大提高收集效率
JVM收集器对应参数
内存分配和回收策略
对象往往在新生代的Eden区分配,Eden区空间不够,发起minor gc,会将eden和一个survivor区的存活对象copy到另一个survivor区,如果另一个survivor区空间不够,存入老年代
大对象会直接进入老年代,比如很长的字符串或很大的数组,大对象对于JVM内存分配是个坏消息,因为大对象需要找到连续内存,否则会触发gc,所以短命的大对象是需要尽量避免的
长期存活的对象进入老年代,对象在新生代每经历一次minor gc,年龄加1, 默认达到15岁会进入老年代
每次Minor GC时,虚拟机会检测每次晋升到老年代的平均大小是否大于老年代当前剩余大小,如果小于,则进行full gc
Java内存管理和垃圾回收的更多相关文章
- Java内存管理及垃圾回收总结
概述 Java和C++的一个很重要的差别在于对内存的管理.Java的自己主动内存管理及垃圾回收技术使得Java程序猿不须要释放废弃对象的内存.从而简化了编程的过程.同一时候也避免了因程序猿的疏漏而导致 ...
- java基础(一):谈谈java内存管理与垃圾回收机制
看了很多java内存管理的文章或者博客,写的要么笼统,要么划分的不正确,且很多文章都千篇一律.例如部分地方将jvm笼统的分为堆.栈.程序计数器,这么分太过于笼统,无法清晰的阐述java的内存管理模型: ...
- Java内存管理 -JVM 垃圾回收
版权声明:本文为博主原创文章,未经博主允许不得转载 一.概述 相比起C和C++的自己回收内存,JAVA要方便得多,因为JVM会为我们自动分配内存以及回收内存. 在之前的JVM 之内存管理 中,我们介绍 ...
- Java基础--Java内存管理与垃圾回收
Java自动内存管理 在讲解内存管理之前,首先需要了解对象和对象引用的区别 对象是类的一个实例,以人这个类为例,Person是我们定义的一个类 public class Person{} publ ...
- java内存管理之垃圾回收及JVM调优
GC(garbage Collector 垃圾收集器)作用:a.内存的动态分配:b.垃圾回收注:Java所承诺的自动内存管理主要是针对对象内存的回收和对象内存的分配. 一.垃圾标记 程序计数器.Jav ...
- Java之美[从菜鸟到高手演变]之JVM内存管理及垃圾回收
很多Java面试的时候,都会问到有关Java垃圾回收的问题,提到垃圾回收肯定要涉及到JVM内存管理机制,Java语言的执行效率一直被C.C++程序员所嘲笑,其实,事实就是这样,Java在执行效率方面确 ...
- JVM原理(Java代码编译和执行的整个过程+JVM内存管理及垃圾回收机制)
转载注明出处: http://blog.csdn.net/cutesource/article/details/5904501 JVM工作原理和特点主要是指操作系统装入JVM是通过jdk中Java.e ...
- java Vamei快速教程22 内存管理和垃圾回收
作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 整个教程中已经不时的出现一些内存管理和垃圾回收的相关知识.这里进行一个小小的总结. ...
- JVM内存管理及垃圾回收【转】
很多Java面试的时候,都会问到有关Java垃圾回收的问题,提到垃圾回收肯定要涉及到JVM内存管理机制,Java语言的执行效率一直被C.C++程序员所嘲笑,其实,事实就是这样,Java在执行效率方面确 ...
随机推荐
- hdu 1166 线段树单点更新
等线段树复习完再做个总结 1101 2 3 4 5 6 7 8 9 10Query 1 3Add 3 6Query 2 7Sub 10 2Add 6 3Query 3 10End Case 1:633 ...
- Debugging Chromium on Windows
转自:https://www.chromium.org/developers/how-tos/debugging-on-windows For Developers > How-Tos & ...
- C++编程学习50个经典网站 强力推荐 (转)
C/C++是最主要的编程语言.这里列出了50名优秀网站和网页清单,这些网站提供c/c++源代码.这份清单提供了源代码的链接以及它们的小说明.我已 尽力包括最佳的C/C++源代码的网站.这不是一个完整的 ...
- SQLServer2008 绑定默认值
默认值或绑定:(newid())值:2E014A19-7AF5-471E-866C-DC5D6DFE59A5 (dateadd(day,(1),getdate()))值: 2014-07-31 20: ...
- Eclipse中导入外部jar包
首先在项目下创建一个文件夹,保存我们的jar包. 在项目名上右击,依次点击[New]-->[Floder],打开新建文件夹窗口 输入文件夹名称[lib],点击[ok].我们通常在lib文件夹 ...
- 看病要排队(stl)
Time Limit: 3000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission( ...
- Google地图接口API之地图事件(四)
演示几个地图事件: 1. 点击标记缩放地图 仍然使用上一遍文章使用的英国伦敦的地图.点用户点击标记时实现缩放地图的功能(点击标记时绑定地图缩放事件). 代码如下: <html> <h ...
- POJ3254 Corn Fields(状压DP)
题目给个n×m的地图,1可以放玉米0不可以,现在要放玉米,玉米上下左右不能相邻,问放法有几种. 当前一行的决策只会影响下一行,所以状压DP之: dp[i][S]表示前i行放完且第i行放玉米的列的集合是 ...
- BZOJ3595 : [Scoi2014]方伯伯的Oj
由于n很大,有2e8,所以不能直接用splay来维护排名 把splay修改一下 每个节点维护一个区间[l,r],表示编号在[l,r]之间的所有点都在这里 需要支持一个takeout操作: 把编号为k的 ...
- COJ983 WZJ的数据结构(负十七)
显然是动态树裸题:O(mlogn) #include<cstdio> #include<cstring> #include<algorithm> #include& ...