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在执行效率方面确 ...
随机推荐
- php jquery ajax
.php文件如下<?php header('Content-Type:text/html;charset=utf-8'); ?> <script type="text ...
- hdu 2191 完全背包
为了挽救灾区同胞的生命,心系灾区同胞的你准备自己采购一些粮食支援灾区,现在假设你一共有资金n元,而市场有m种大米,每种大米都是袋装产品,其价格不等,并且只能整袋购买.请问:你用有限的资金最多能采购多少 ...
- sina sae 部署 java ssh 项目
转自:http://jacobcookie.iteye.com/blog/1876798 1. 在sae上使用struts,需要添加的Listener,在com.company.listener中添加 ...
- JS中使用EL表达式
转自:http://blog.csdn.net/monkeyking1987/article/details/17146951 分两种情况 1. JS代码在JSP页面中, 这可以直接使用EL表达式. ...
- Java中的Timer和TimerTask在Android中的用法(转)
转自:http://blog.csdn.net/zuolongsnail/article/details/8168689 在开发中我们有时会有这样的需求,即在固定的每隔一段时间执行某一个任务.比如UI ...
- SqlServer 函数 大全
sql server使用convert来取得datetime日期数据 sql server使用convert来取得datetime日期数据,以下实例包含各种日期格式的转换 语句及查询结果: Selec ...
- 用JAXP的dom方式解析XML文件
用JAXP的dom方式解析XML文件,实现增删改查操作 dom方式解析XML原理 XML文件 <?xml version="1.0" encoding="UTF-8 ...
- Failed to load or instantiate
Failed to load or instantiate: add this code in your xml: xmlns:android="http://schemas.android ...
- 【转】apache kafka技术分享系列(目录索引)
转自: http://blog.csdn.net/lizhitao/article/details/39499283 估计大神会不定期更新,所以还是访问这个链接看最新的目录list比较好 apa ...
- 简单几何(凸包+枚举) POJ 1873 The Fortified Forest
题目传送门 题意:砍掉一些树,用它们做成篱笆把剩余的树围起来,问最小价值 分析:数据量不大,考虑状态压缩暴力枚举,求凸包以及计算凸包长度.虽说是水题,毕竟是final,自己状压的最大情况写错了,而且忘 ...