JVM 垃圾回收机制
首先JVM的内存结构包括五大区域: 程序计数器、虚拟机栈、本地方法栈、方法区、堆区。其中程序计数器、虚拟机栈和本地方法栈3个区域随线程启动与销毁, 因此这几个区域的内存分配和回收都具有确定性,不需要过多考虑回收的问题。而Java堆区和方法区则不一样,这部分内存的分配和回收是动态的,正式垃圾回收需要关注的部分。
垃圾回收在堆内存进行回收前, 要先确定区域的哪些对象是可以被回收的、那些对象暂时还不能回收,下面谈一谈判断对象是否存活的算法。
判断对象是否存活的算法
1.引用计数算法
引用计数算法:堆中的每个对象实例都有一个引用计数器,当一个对象被创建时,就将该对象实例分配给一个变量,该引用计数器设置为1,当任何其他变量被赋值为这个对象的引用时,计数加1,当一个对象实例的某个引用超过了生命周期或被赋为一个新值时, 引用计数减1。
任何引用计数器为0的对象实例都可以进行垃圾回收。当一个对象实例被垃圾回收时,它引用的所有对象实例引用计数器减1.
优点:引用计数器可以很快的执行,对程序不需要长时间的打断
缺点:无法检测出循环引用。如对象A有对象B的引用,对象B又有对象A的引用,这样他们的引用计数永远都不为0
2.可达性分析算法
可达性算法:将所有的引用关系看作一张图,从一个节点GC Root开始,寻找对应的引用节点,找到后继续寻找这个节点的引用节点,当所有引用节点寻找完毕后,剩余的节点就被认为是没有被引用的节点,即无用节点,无用节点被判定为可回收对象。
Java中可以作为GC Root的包括下面几种:
- 虚拟机栈中的引用对象
- 方法区中类静态属性引用的对象
- 方法区中常量引用的对象
- 本地方法栈中引用的对象
对于Java中的引用类型可以看这篇文章Java 控制类的引用类型,合理使用内存
常用的垃圾回收算法
1.标记-清除算法
标记-清除算法采用从根集合(GC Roots)进行扫描,对存活的对象进行标记,标记完毕后,再扫描整个空间中未被标记的对象,进行垃圾回收
这种算法实现起来比较容易,但是会造成内存碎片
2.标记-复制算法
复制算法是为了解决标记-清除算法的缺陷而提出的。
它将内存划分为大小相等的两块,每次只使用其中的一块。当这A快内存用完了,就将还存活的对象复制到B块上面,然后把A块的内存空间一次性清理掉
这种算法虽然实现简单,运行高效且不易产生内存碎片,但是却对内存空间的使用做出了高昂的代价,因为能使用的空间缩减为原来的一半。很显然,复制算法的效率跟存活对象的数量有很大关联,若存活对象很多,那么效率将大大降低
3.标记-整理算法
该算法是为了解决复制算法的缺陷,充分利用内存空间而提出的。
该算法与标记-清除算法一样,但是在完成标记后,不直接清理可回收对象,而是将存活对象全部向一端移动,接着清理掉边界以外的内存。
4.分代收集算法
分代收集算法是目前大部分JVM的垃圾收集器采用的算法。其核心思想是根据对象存活的生命周期将内存划分为若干个不同的区域。
将其分为年轻代、老年代和永久代。然后根据不同的区域采用合适的收集算法。
Java一般将堆区分为年轻代和老年代,将方法区划为永久代。
下面对不同的年龄代进行简单说明
年轻代:新创建的对象都存放在这里。因为年轻代会频繁的进行GC清理,JVM在年轻代采用的是标记-复制算法,先标记出存活的实例,然后清除掉无用实例,将存活的实例根据年龄(每个实例被经历一次GC后年龄会加1)拷贝到不同的年龄代。
老年代:老年代中是经历了N此垃圾祸首后仍然存活的对象,其中的N由JVM的参数决定。这块内存区域一般大于年轻代。GC发生的次数也比年轻代要少。
永久代:用于存放静态文件,如Java类、方法等。为方法区。
方法区主要回收的内容有:废弃的常量、无用的类,对与废弃常量可以同过引用的可达性判断,但是对于无用类需要同时满足以下3个条件:
- 该类的所有实例都已经被回收了
- 加载该类的 ClassLoader 已经被回收了
- 该类对应的 java.lang.Class 对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法
GC在什么时候触发
GC在优先级最低的线程中运行,一般在应用程序空闲时被调用。当内存不足时才会主动调用
因为对象进行了分代处理,因此垃圾回收区域、时间也不一样。GC有如下两种:
1.Scavenge GC
一般情况下,当新对象生成,并且在年轻代申请空间失败时,会触发Scavenge GC, 对年轻代进行垃圾回收。这种方式的GC不会影响到老年代。因为大部分对象都是年轻代开始的,同时年轻代内存不会分配的很大,所有年轻代的GC会频繁的进行。所以在这里要使用速度快、效率高的算法,使其空间尽快空出来。
若GC一次后仍不能满足内存分配,JVM会进行二次GC,若仍无法满足,则报“out of memory"的错误,Java应用将停止
2.Full GC
对整个内存进行整理,包括年轻代、老年代和永久代,所以Full GC比Scavenge GC要慢, 因此应该尽量减少Full GC的次数。以下可能引发Full GC的原因:
- 老年代被写满
- 永久代被写满
- System.gc()被显示调用
- 上一次GC后堆的各域分配策略动态变化。
Java的垃圾回收介绍到这,下面在说说如何在程序中减少GC的开销的几个建议:
- 不要显式调用System.gc()。此函数建议JVM进行GC,虽然只是建议,但是大多数情况下会触发GC,增加了间歇性停顿的次数,大大影响系统的性能
- 尽量减少临时对象的使用。也就是减少Scavenge GC执行的机会
- 对象不用时最好显式置为null。将不用的对象置为null,有利于GC收集器判定,从而提高GC的效率
- 尽量减少静态对象变量。静态变量属于全局变量,不会被GC祸首。
- 能有基本类型的就不要用包装类。基本类型变量栈用的内存资源比对应的包装类要少的多
- 使用StringBuffer 而不是String类累加字符串。因为堆String类型进行加的时候,会创建新的String对象,而StringBuffer是可变长的,在原有基础上进行扩增,不会产生中间对象
- 分散对象创建或删除的时间。集中在短时间内大量创建新对象,特别是大对象,会突然需要大量内存,JVM在面临这种情况时只能进行GC,以回收内存或整合内存碎片,从而增加GC的频率。集中删除对象,道理也是一样的。它使得突然出现了大量的垃圾对象,空闲空间必然减少,从而大大增加了下一次创建新对象时强制主GC的机会。
JVM 垃圾回收机制的更多相关文章
- JVM垃圾回收机制总结:调优方法
转载: JVM垃圾回收机制总结:调优方法 JVM 优化经验总结 JVM 垃圾回收器工作原理及使用实例介绍
- JVM内存管理和JVM垃圾回收机制
JVM内存管理和JVM垃圾回收机制(1) 这里向大家描述一下JVM学习笔记之JVM内存管理和JVM垃圾回收的概念,JVM内存结构由堆.栈.本地方法栈.方法区等部分组成,另外JVM分别对新生代和旧生代采 ...
- JVM垃圾回收机制概述
JVM垃圾回收机制概述 1.定义 是指JVM用于释放那些不再使用的对象所占用的内存. 2.方式 2.1引用计数(早期) 当引用程序创建引用以及引用超出范围时,JVM必须适当增减引用数.当某个对象的引用 ...
- Java虚拟机学习笔记——JVM垃圾回收机制
Java虚拟机学习笔记——JVM垃圾回收机制 Java垃圾回收基于虚拟机的自动内存管理机制,我们不需要为每一个对象进行释放内存,不容易发生内存泄漏和内存溢出问题. 但是自动内存管理机制不是万能药,我们 ...
- JVM基础系列第8讲:JVM 垃圾回收机制
在第 6 讲中我们说到 Java 虚拟机的内存结构,提到了这部分的规范其实是由<Java 虚拟机规范>指定的,每个 Java 虚拟机可能都有不同的实现.其实涉及到 Java 虚拟机的内存, ...
- JVM内存管理、JVM垃圾回收机制、新生代、老年代以及永久代
内存模型 JVM运行时数据区由程序计数器.堆.虚拟机栈.本地方法栈.方法区部分组成,结构图如下所示. JVM内存结构由程序计数器.堆.栈.本地方法栈.方法区等部分组成,结构图如下所示: 1)程序计数器 ...
- JVM 垃圾回收机制和常见算法和 JVM 的内存结构和内存分配(面试题)
一.JVM 垃圾回收机制和常见算法 Sun 公司只定义了垃圾回收机制规则而不局限于其实现算法,因此不同厂商生产的虚拟机采用的算法也不尽相同.GC(Garbage Collector)在回收对象前首先必 ...
- JVM垃圾回收机制和常用算法
由于疫情的原因,所以目前一直在家远程办公,所以很多时间在刷面试题,发现2019大厂的面试虽然种类很多,但是总结了一下发现主要是这几点:算法和数据结构. JVM.集合.多线程.数据库这几点在面试的时候比 ...
- 真的可惜,四面阿里,结果我被JVM垃圾回收机制与 OOM异常卡住了
前言 为什么需要垃圾回收 首先我们来聊聊为什么会需要垃圾回收,假设我们不进行垃圾回收会造成什么后果,我们举一个简单的例子 我们住在一个房子里面,我们每天都在里面生活,然后垃圾都丢在房子里面,又不打扫, ...
- java JVM垃圾回收机制
Java语言出来之前,大家都在拼命的写C或者C++的程序,而此时存在一个很大的矛盾,C++等语言创建对象要不断的去开辟空间,不用的时候有需要不断的去释放控件,既要写构造函数,又要写析构函数,很多时候都 ...
随机推荐
- Web browser发展演变
浏览器是指可以显示网页服务器或者文件系统的HTML文件内容,并让用户与这些文件交互的一种软件.网页浏览器主要通过HTTP协议与网页服务器交互并获取网页,这些网页由URL指定,文件格式通常为HTML.大 ...
- 包建强的培训课程(3):App竞品技术分析
@import url(http://i.cnblogs.com/Load.ashx?type=style&file=SyntaxHighlighter.css);@import url(/c ...
- 玩转Kafka的生产者——分区器与多线程
上篇文章学习kafka的基本安装和基础概念,本文主要是学习kafka的常用API.其中包括生产者和消费者, 多线程生产者,多线程消费者,自定义分区等,当然还包括一些避坑指南. 首发于个人网站:链接地址 ...
- 前端开发JS白板编程题目若干
在前端开发参加面试的时候,无论是校招还是社招,往往都会碰到让我们直接在白纸或者白板上手撸代码的题目.由于是手撸代码,这些题目肯定不会过于复杂和冗长,否则面试那么一小会时间根本写不完.本文总结了几个我本 ...
- java责任链模式及项目实际运用
1.前言 上次我们认识了java责任链模式的设计,那么接下来将给大家展示责任链模式项目中的实际运用.如何快速搭建责任链模式的项目中运用. 2.简单技术准备 我们要在项目中使用借助这样的几个知识的组合运 ...
- OkHttp 设置 User-Agent 教程
我们知道OkHttp走的并不是原生的http请求,因此在header里面并没有真正的User-Agent,而是“okhttp/版本号”这样的字符串,因为后台需要统计信息,要求传入正确的User-Age ...
- ubuntu 16.04 下安装NVDIA的库(CUDA+CUDNN)
立志学习神经网络的同学,为了让它能够跑快一点,估计英伟达的GPU是要折腾一番的. 首先看一下什么是CUDA ? CUDA(Compute Unified Device Architecture),是显 ...
- java基础-3
java基础-3 API Application Programming Interfaces --- 应用程序接口 Object 顶级父类 Bin --- 二进制 Oct --- 八进制 Dec ...
- 【MML】华为MML AAA接口联调,Java版本
1.我们先设置一些常量数据 package cn.cutter.ztesoft.HuWeiMML.constrant; /** * @description: AAA接口常量设置 * @author: ...
- Java 基础:hashCode方法
Writer:BYSocket(泥沙砖瓦浆木匠) 微博:BYSocket 豆瓣:BYSocket 一.前言 泥瓦匠最近被项目搞的天昏地暗.发现有些要给自己一些目标,关于技术的目标: 专注很重要.专注J ...