深入理解JVM(三)垃圾收集器和内存分配策略
3.1 关于垃圾收集和内存分配
垃圾收集和内存分配主要针对的区域是Java虚拟机中的堆和方法区;
3.2 如何判断对象是否“存活”(存活判定算法)
垃圾收集器在回收对象前判断其是否“存活”的两个算法:
1.引用计数算法:一个对象在被引用之后这个计数器就加1,不被引用之后则减1,如果是0,那么就被回收,这个一般不被主流Java虚拟机所使用,原因:对象的循环引用会导致计数器始终不为0,那么就无法回收。
2.可达性分析算法:通过GC roots对象作为起点,向下搜索和它进行有效链接的对象,如果对象最终不和GC roots所链接,那么它(们)将会被回收。GC roots对象:①栈中的引用;②方法区中的引用;③本地方法中的引用;
3.对象的引用:
①强引用:只要引用A a = new A()存在,就不会被回收,这就是强引用;
②软引用:可能会被回收的引用,如果内存不足则会进行回收,通过实现SoftReference类;
③弱引用:只能活到下一次垃圾收集之前,比软引用更弱,通过实现WeakReference类;
④虚引用:最弱的引用,此引用无法通过引用获得对象,作用就是此引用的对象在被回收时会收到一个系统通知;
4.对象的存亡与finalize():当对象没有重写finalize()方法或虚拟机已经执行过finalize()方法时(finalize()方法只能执行一次),那么这个对象会“不需要执行finalize()”,否则对象会被加入到一个低优先级(多线程的)的队列去执行finalize()方法,finalize()方法是对象被回收前最后的挣扎,如果对象能够再被引用则能自救不被回收,但是最好不用这个方法,因为其不确定性很强。
5.方法区的回收:一般来说虚拟机可以不实现方法区(老年代)的回收,因为其回收性价比低,不如堆的回收效率高(75%以上),但是有一些大量使用反射等机制的情况下就需要了。回收常量还比较轻松,而回收class的条件苛刻:①这个class的实例对象已被回收;②加载这个class的ClassLoader已被回收;③class对象本身没有再被引用;然后才可以被回收,而且要设置一些参数才行。
3.3 垃圾收集算法:
1.标记-清理算法:先标记对象再清理,缺点:效率低,清理后的空间碎片化,容易出现连续空间不足问题,后续的算法在此算法上进行改良;
2.复制算法:分两块区域,将存活的对象按顺序复制到另一块内存中,然后清理掉此内存。主要用于清理新生代,因为新生代的存活时间不长,一般商业虚拟机中,80%的Eden内存空间用于存放新生代,另外两个Survivor空间分别占据10%的空间,每次都将存活的新生代对象从Eden和一块Survivor中复制到另一个Survivor空间中,然后进行清理,但是不保证每次都能保证Survivor空间足够大,所以需要一块分配担保的空间来应对Survivor空间不够大的问题;
3.标记-整理算法:从标记-清理算法优化而来,先标记对象,清理之后对内存空间进行整理,存活的对象往一端移动,然后清理掉边界外的对象,此算法主要用于存活较久的老年代;
4.分代收集算法:根据对象存活周期将内存划分为几块,一般就是新生代和老年代。然后根据不同的代来选择不同的算法,新生代使用复制算法,老年代使用标记-整理算法。
3.4 HotSpot的算法实现(如何发起内存回收):
1.枚举根节点:GC roots,GC时虚拟机“停顿”,HotSpot使用OopMap保存引用的起始位置和偏移量,避免了一个不漏地检查执行上下文和全局引用位置;(总结:判断对象的引用存在)
2.安全点(Safepoint):程序执行到安全点时才会停顿更新OopMap进行GC,安全点所在位置:程序指令需要较长时间执行的,如方法调用、循环跳转、异常跳转,线程在安全点中断策略:①抢先式中断:GC时中断所有线程,判断是否到达安全点,如果没有则回复线程让它继续到安全点;②主动式中断:在安全点上加一个标记,线程会轮训这个标记,如果有这个标记,则GC。(总结:判断在哪里进行GC);
3.安全区域(Safe Region):安全点需要线程的运行,但是当线程不运行时(sleep)需要GC怎么办呢,则通过安全区域,线程执行到安全区域时会做标识,安全区域内的对象的引用是不会发生改变的(安全区域特征),所以GC对安全区域内引用的对象没有影响;
3.5 垃圾收集器:
垃圾收集器没有绝对的优劣,在不同的情况下有不同的表现
1.Serial:最基本的、发展最久的,GC年轻代,单一而高效但是线程停顿,GC时停止所有其他线程(Stop The World);适用:Client模式;
2.ParNew:Serial的多线程版,其他方面基本上没有区别,GC年轻代;适用:Server模式,适用于多核CPU的计算机;配置:限制线程数-XX:ParallelGCThreads() ;
3.Parallel Scavenge:并行的GC收集器,GC年轻代,可以控制吞吐量的GC收集器;配置:①-XX:MaxGCPauseMills 最大停顿毫秒数,牺牲新生代内存大小,来提高速度,但会导致吞吐量减少;②-XX:GCTimeRatio 垃圾收集的时间比重;③-XX:+UserAdaptiveSizePolicy 开关参数,开启之后就无需配置新生代大小(-Xmn)、Eden和Survivor区的比重(-XX:SurvivorRatio)、晋升老年代的年龄(-XX:PretenureSizeThreshold),注意开启此配置之后给堆分配好最大内存(-Xmx);
小结:以上三种GC收集器都是用于回收年轻代的,回收的算法都是用的复制算法;
4.Serial Old:是Serial的GC老年代的版本,回收的算法是标记-整理算法;
5.Parallel Old:并行的GC收集器,是Parallel Scavenge的老年代版本,和Parallel Scavenge组成“吞吐量优先”组合;
6.CMS(Concurrent Mark Sweep):并发GC收集器
(1)优点:这是一个以停顿时间最短为目标的GC收集器,对于需要快速响应的系统来说非常;
(2)缺点:
①需要和运行线程抢占资源,会降低吞吐量,对单核计算机不友好;
②无法处理浮动垃圾,可能导致“Concurrent Mode Falure”然后进行Full GC,浮动垃圾是伴随CMS处理过程中产生的新的对象,如果下次运行的时候,老年代占用太高而导致CMS自身需要的内存不足时就会出现“Concurrent Mode Falure”然后使用备用的Serial Old收集器,配置:-XX:+CMSInitiationOccupancyFraction 配置老年代GC的内存阈值百分比;
③产生碎片空间,由于其算法是标记-清理,所以出现大对象时可能连续空间不足,就会提前触发Full GC,通过配置:-XX:+UserCMSCompactAtFullCollection(默认开启的)CMS顶不住时整理碎片(此处需要停顿无法并发)、-XX:CMSFullGCsBeforeCompaction:多少次Full GC之后才进行压缩整理(默认0,每次都整理);
7.G1(Garbage-first):G1收集器是一个最新的研究成果,G1是一个面向服务端的收集器
(1)它是一个并行并发的GC收集器,可降低“Stop-The-World”的停顿时间,可同时收集年轻代和老年代;
(2)整理空间:总的来说使用“标记-整理”算法,而在Region区域之间采用复制算法,高效地保证了没有碎片产生;
(3)可预测的停顿:可以指定M毫秒内,GC停顿的时间不超过N毫秒;
(4)内存分区,优先收集优先列表前列的Region(G1收集器名字的由来),使用Remambered Set保存有互相引用的区域地址,针对这个Set的区域会进行GC;
8.GC日志:-XX:+PrintGCDetails 打印GC日志的配置
33.125: [GC [DefNew: 3324K->152K(3712K), 0.0025925 secs] 3324K->152K(11904K), 0.0031680 secs]
100.667: [Full GC [Tenured: 0K->210K(10240K), 0.0149142 secs] 4603K->210K(19456K), [Perm : 2999K->2999K(21248K)], 0.0150007 secs] [Times: user=0.01 sys=0.00, real=0.02 secs]
(1)33.125:从虚拟机启动到这次GC的秒数;
(2)Full GC:代表发生了停顿“Stop-The-World”的GC;
(3)DefNew:不同的收集器对不同的新生代和老年代有不同的叫法,这里是新生代;
(4)3324K->152K(3712K):回收前占用内存大小 -> 回收后占用内存大小(总的内存大小);
(5)0.0031680:此次GC的秒数;
9.GC配置参数总结
3.6 内存分配与回收策略
1.对象优先在Eden分配:
(1)Minor GC:在新生代中的GC,一般速度都很快,且频率高;
(2)Major GC/Full GC:在老年代中的GC,经常伴随至少一次Minor GC,速度慢,频率低,速度比Minor GC慢10倍以上;
2.大对象直接进入老年代:
(1)大对象:例如:很长字符串,byte数组;
(2)-XX:PretenureSizeThreshold xx:超过xx字节的对象直接进入到老年代,只有Serial和ParNew GC收集器可以进行此配置,必要的情况下可以使用ParNew + CMS的组合;
3.长期存活的对象进入老年代:
-XX:MaxTenuringThreshold=xx:对象在新生代经历过xx次Minor GC还没有被回收,那么此对象进入老年代;
4.动态对象年龄判定:
当某一年龄的所有对象占据的Survivor空间大于一半时,年龄大于这些对象的对象则会直接进入到老年代,而无需等到-XX:MaxTenuringThreshold的年龄;
5.空间分配担保:
(1)Minor GC前都会有一次对老年代最大连续空间是否大于新生代所有对象总空间的判断,如果大于,则看是否开启HandlePromptionFailure是否为true,如果是,则检查老年代最大连续空间是否大于可晋升老年代的对象的总空间,如果大于则进行一次有风险的Minor GC,否则如果为false或者老年代最大连续空间小于时,则进行一次Full GC;
(2)而(1)中所说的有风险的Minor GC就是空间分配担保,当Survivor空间不足以容纳Minor GC之后的新生代时,则向老年代晋升(空间分配担保);
深入理解JVM(三)垃圾收集器和内存分配策略的更多相关文章
- 深入理解JVM:垃圾收集器与内存分配策略
堆里面存放着Java世界差点儿全部的对象实例,垃圾收集器在对堆进行回收前.第一件事情就是要确定这些对象之中哪些还存活,哪些已经死去.推断对象的生命周期是否结束有下面几种方法 引用计数法 详细操作是给对 ...
- 深入理解java虚拟机----->垃圾收集器与内存分配策略(下)
1. 前言 内存分配与回收策略 JVM堆的结构分析(新生代.老年代.永久代) 对象优先在Eden分配 大对象直接进入老年代 长期存活的对象将进入老年代 动态对象年龄判定 空间分配担保 2. 垃圾 ...
- 【转载】JVM 学习——垃圾收集器与内存分配策略
本文主要是对<深入理解java虚拟机 第二版>第三章部分做的总结,文章中大部分内容都来自这章内容,也是博客 JVM 学习的第二部分. 简述 说到垃圾收集(Garbage Collectio ...
- 深入理解JAVA虚拟机 垃圾收集器和内存分配策略
引用计数算法 很多教科书判断对象是否存活的算法是这样的:给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1:当引用失效时,计数器值就减1:任何时刻计数器都为0的对象就是不可能再被使用的 ...
- [深入理解Java虚拟机]<垃圾收集器与内存分配策略>
Overview 垃圾收集考虑三件事: 哪些内存需要回收? 什么时候回收? 如何回收? 重点考虑Java堆中动态分配和回收的内存. Is Object alive? 引用计数法 给对象添加一个引用计数 ...
- JVM(3) 垃圾收集器与内存分配策略
一.垃圾收集的概念 在Java虚拟机运行时数据区中程序计数器.虚拟机栈和本地方法栈3个区域随线程而生,随线程而灭:栈中的栈帧随着方法的进入和退出而有条不紊地执行着出栈和入栈操作,每一个栈帧中分配多少内 ...
- 《深入理解Java虚拟机》(三)垃圾收集器与内存分配策略
垃圾收集器与内存分配策略 详解 3.1 概述 本文参考的是周志明的 <深入理解Java虚拟机>第三章 ,为了整理思路,简单记录一下,方便后期查阅. 3.2 对象已死吗 在垃圾收集器进行回收 ...
- 《深入理解java虚拟机》第三章 垃圾收集器与内存分配策略
第三章 垃圾收集器与内存分配策略 3.1 概述 哪些内存需要回收 何时回收 如何回收 程序计数器.虚拟机栈.本地方法栈3个区域随线程而生灭. java堆和方法区的内存需要回收. 3.2 对象已死吗 ...
- 深入理解java虚拟机_第三章(上)----->垃圾收集器与内存分配策略
1. 前言 这一版块内容比较多,分为两篇文章来做笔记.本文讲述上半部分垃圾收集部分;下一篇文章写内存分配部分. 概述 对象已死吗? 引用技术算法 可达性分析算法 再谈引用 两次标记 回收方法区 2. ...
随机推荐
- SpringSecurity-UsernamePasswordAuthenticationFilter的作用
UsernamePasswordAuthenticationFilter应该是我们最关注的Filter,因为它实现了我们最常用的基于用户名和密码的认证逻辑. 先看一下一个常用的form-login配置 ...
- 【spring】之基于注解@ComponentScan的一些使用
基于xml形式ComponentScan的使用如下 <context:component-scan base-package="com.luna" use-default-f ...
- Python循环语句之break与continue的用法
摘自原文章: http://www.jb51.net/article/73383.htm Python break 语句Python break语句,就像在C语言中,打破了最小封闭for或while循 ...
- mysql-查询存在主表但是不存在副标的数据
A.B两表,找出ID字段中,存在A表,但是不存在B表的数据.A表总共13w数据,去重后大约3W条数据,B表有2W条数据,且B表的ID字段有索引. 方法一 使用 not in ,容易理解,效率低 ~执 ...
- jquery如何实现当页面下拉到一定位置时,右下角出现回到顶部图标
渐进式返回顶部
- python基础之数字、字符串、列表、元组、字典
Python基础二: 1.运算符: 判断某个东西是否在某个东西里面包含: in 为真 not in 为假 (1).算术运算符: 运算符 描述 实例 + 加 表示两个对象相加 a + b输出结果3 ...
- leetcode1031
class Solution(object): def getMaxByCount(self,A,maxlen): curmax = 0 curmax = sum(A[:maxlen]) bigmax ...
- python接收html页面上传的文件
使用的 flask, 没有安装的先安装 pip install flask 示例代码:示例没有自动创建静态文件夹,需要自己在同级 创建一个名为 static 的文件夹来存放上传的文件 示例展示为图片 ...
- Sql入门学习——关系范式
--------关系 --------范式 一.三种关系 1.一对一关系 关系数据库中,第一个表中的单个行只可以与第二个表中的一个行相关,且第二个表中的一个行也只可以与第一个表中的一个行相关. 2.一 ...
- TensorFlow学习之四
Tensorflow一些常用基本概念与函数(1) 摘要:本文主要对tf的一些常用概念与方法进行描述. 1.tensorflow的基本运作 为了快速的熟悉TensorFlow编程,下面从一段简单的代码开 ...