- 浏览: 374606 次
- 性别:
- 来自: 杭州
文章分类
最新评论
-
surpassno:
很不错,学习了
一个对象占用多少字节? -
ysyzww:
你这么牛逼,你父母知道吗
maven使用技巧 -
妖人不要跑:
JDK中反序列化对象的过程(ObjectInputStream#readObject) -
lanhz:
谢楼主,构建成功了
Mac OSX 10.9 上build openjdk8和openjdk7 -
zqb666kkk:
通过了吗 ?????
淘宝北京专场java面试题(2011-12-31)
一、概述
Java最大的一个特点就是不用开发人员手动释放对象的内存,这些任务就交给了jvm来做。垃圾收集器有很多分类,如按照并行(ParNew、Parallel Scavenage、Parallel Old、CMS并发标记阶段、g1)、并发(cms并发标记和并发清理阶段、g2)和串行(Serial、Serial Old/PS Mark-Sweep),按照算法分引用计数和跟踪算法,按照性能指标比如吞吐量(Parallel Scavenge、Parallel Old)、低停顿(CMS)、增量式(G1)。
HotSpotVM的内存结构
二、基本算法
gc实现虽然很多,像串行、并行、并发、分代,但是最基本的算法却只有几种:引用计数、标记-清除算法、拷贝和整理,其中拷贝和整理算法还是以标记清除为基础的。
1、引用计数
每个对象记录被引用的次数,每多一个引用计数器加一,少一个引用计数器减一。如果引用次数为0了,就表示可以回收了。但是这里有一个问题,对象循环引用的时候,没有办法判断该不该回收。例如A引用B,B引用A,但是A、B都没有被别的对象引用,此时A、B计数器都不为0,但是也应该回收的。
2、标记-清除算法Mark-Clean
通过GC Roots遍历被引用的对象,标记为可达,未被遍历到的对象就是不再被引用的对象,然后遍历一遍把不再被引用的对象占用的内存回收掉。但是这种算法会带来一个问题,就是内存碎片问题,如果每次按照对象大小分配,内存用完之后回收,可用空间零散散布于这个堆中,下次申请比较大的内存的时候,可能就申请不到很大的连续空间,只有一堆不连续的小空间,此时申请内存就会失败,而从外部来看,堆中总共还有足够的空闲空间来分配。
对于JVM,GC Roots包括栈中引用的对象、方法区类变量引用的对象(类型信息也要被回收的,怎么会作为roots呢?)、本地方法栈引用的对象(还有什么?)
3、拷贝算法Copy
针对标记-清除算法的碎片问题,出现了一种改进的算法,把堆内存划分为两块,每次使用一块,当一块满的时候,就去标记使用中的对象,然后把使用中的对象拷贝到另外一个块里连续的内存空间中,然后把这个块的内存全部回收,后续创建对象申请空间就直接在另外一个块中申请。然后另外一个空间满的时候重复此过程。此算法解决了内存碎片问题,但是会降低内存的使用效率,一直都只有一半的空间被使用。同时考虑这样一个问题,如果有些对象一直被使用,如java.lang.Object这种在jvm的生命周期中一直会被使用的对象,copy来copy去的还是效率很低的。
4、标记-清理算法(也叫压缩算法)Mark-Sweep
针对拷贝算法的空间使用效率问题,标记整理算法给出了解决办法。在内存空间占满的时候,先通过GC Roots标记出存活的对象,然后把存活的对象从堆空间的一端,移动到另外一端,并使得他们占用的内存空间是连续的。移动完后,另一端就是一大块连续的空间,后续分配就在连续的空间进行,如果再次满了,就重复标记整理过程。但是这种算法依然存在拷贝算法存在的一个问题,就是长期存活的对象会被移动来移动去的。
5、分代收集算法
针对标记-清理算法中出现的问题,分代收集算法给出了解决办法。分代收集算法把根据对象存活的时间把堆内存空间多个部分,一般有新生代、中生代和老生代,不同的世代中的对象一般会用不同的算法来收集,如新生代和中生代很多朝生夕死的对象用拷贝算法拷贝效率也比较高,收集频率也比较高。老生代因为存活时间都很长了,并且可能一直存活下去,拷贝来拷贝去拷贝量很大,所以一般采用标记整理算法,收集的频率一般也比较低。
6、自适应算法
不同情况下,不同的收集器工作的好坏程度不一样,可以根据运行时状况动态调整。另外堆不同区域可以使用不同的算法。
三、垃圾收集器
垃圾收集器是特定于实现的收集器,都是基于以上算法实现的。不同的虚拟机,实现的细节也不太相同,此处只是一些概念和思想上的讲解。
1、Serial
Serial是新生代的单线程垃圾收集器,使用Copy算法。当进行垃圾收集的时候,别的线程都停止工作,Stop the World。这种收集器在多cpu上无法充分利用cpu资源,另外由于是单线程工作,停止时间也比较长。
在HotSpot JVM中使用-XX:+UserSerialGC会启动该收集器,同时老生代用SerialOld。是client模式默认的收集器。
该收集器可以跟老生代的Serial Old、CMS协同工作。
2、SerialOld
使用Mark-Sweep算法,是Serial的老生代GC收集器版本,单线程,Stop the World。
该收集器可以跟新生代的Serial、SerNew、Parallel Scavanage 协同工作。
该收集器还作为CMS失败时候的后备收集器。
3、ParNew
新生代收集器,是Serial的多线程版本,可以充分利用机器的cpu,收集的时候Stop the World。在单线程下也可以利用多线程,但是效率可能不如Serial,如果是多cpu,效率会很高。
在HotSpot JVM中使用-XX:UseParNew会启用该收集器,同时老生代使用SerialOld。
该收集器可以跟老生代的SerialOld、CMS协作收集。
4、Parallel Scavenge
新生代的收集器,也叫PS Scavenge,比起ParNew,此收集器的目的是达到一个可控的吞吐量,所以也叫做吞吐量优先收集器。可以通过参数-XX:+UseAdaptiveSizePolicy使用自适应内存调节策略,同时设置上-XX:MaxGCPauseMillis设置最大停机时间,通过-XX:GCTimeRatio设置吞吐量。
通过参数-XX:+UseParallelGC启用该收集器,此时老生代使用Serial Old(也叫PS MarkSweep,是在Serial Old的外边加了一层壳,本质上还是serial,很多地方也这么叫)
此收集器可以跟老生代Serial Old和Parallel Old协作。
5、Parallel Old
老生代并行收集器,Parallel Scavenge的老生代版本,吞吐量优先(但是没有相关参数设置老生代度量的?)
通过参数-XX:+UseParallelOldGC启用,新生代是Parallel Scavenage收集器。
注意:在jconsole中看到,-XX:+UseParallelOldGC时候老生代的内存收集器是PS MarkSweep,不是Parallel Old这个名字,原因这里有。
6、CMS/ConcurrentMarkSweep
老生代收集器,Concurrent Low Pause Collector,收集阶段分为四个阶段:
a、初始标记(initial mark),串行执行。从GC roots开始标记由roots直接关联的对象
b、并发标记(concurrent mark),并发执行。用户线程和标记线程同时工作,根据上一步标记出的对象进一步标记整个引用链。
c、重新标记(remark),串行执行。在并发标记完成后,再进行一次并行标记,处理在并发标记阶段引用改变的对象。
d、并发收集(concurrent mark),并发收集。gc线程跟用户线程一起运行,收集内存。
此收集器用的是Mark-Sweep算法,会导致内存随便,可能会出现总内存足够,但是分配某个大小的内存时候没有空间的情况,此时就是用Serial Old收集器作为失败后的收集器来收集。
通过参数-XX:+UseConcMarkSweepGC启用该收集器,新生代收集器默认是ParNew,并发模式失败后是用Serial Old收集器。
7、G1收集器
Garbage First,将在jdk1.7中发布。比起cms有两个优势,一是g1基于mark-compact算法,不会产生内存碎片;二是可以精确控制停顿。该收集器是把整个堆(新生代、老生代)划分成多个固定大小的区域,每次根据允许停顿的时间去收集垃圾最多的区域。
四、终结
finalize方法是对象被gc前必须运行的。在gc时候,发现对象已经不再存活,就会判断对象是否有finalize方法, 如果有并且没有被运行过,就会运行该方法。运行该方法的时候对象可能会复活,比如把自己赋值给了一静态变量,或者一个存活对象的某个属性,这样该对象就不会被回收了。
运行完终结方法后,垃圾收集器会再次扫描不再被引用的对象,如果这次发现前一遍扫描到的对象仍然没有存活,就会释放掉它所占用的内存。终结方法只会运行一次,等下次该对象再次不在被引用的时候,就不会在运行该方法。
在Hotspot VM中,调用终结方法的是一个Finalizer线程,要终结的对象都被放到了F-Queue中,如果finalize方法运行时间过长,虚拟机会终止方法运行并回收对象的。
五、对象的可触及性和引用
堆中对象可以分为6种状态:强可触及、软可触及、弱可触及、影子可触及、可复活和不可触及。是根据从GC roots对象通过什么方式触及到该对象的。软、弱、影子可触及是通过Reference对象实现的。
软引用:内存不够的时候(将要OOM之前)会回收这些对象占用的内存,软引用可以用作缓存。
弱引用:每次gc的时候都会回收该引用引用的对象,弱引用可用作规范映射。
影子引用:放进影子引用的对象不能再在程序中得到,但是该对象被gc的时候你可以得到通知。
三个引用对象都可以关联队列,其中影子引用必须关联。
发表评论
-
一次Direct buffer memory引发的OutOfMemoryError问题排查
2014-10-28 17:22 0留坑位 -
jvm运行期打印汇编信息
2014-04-09 23:00 3148如果只在jvm参数中加入-XX:+Prin ... -
Mac OSX 10.9 上build openjdk8和openjdk7
2014-03-29 18:29 14276先分享下自己build出来的fastdeb ... -
内存充足情况下应用一直CMS GC的问题分析
2014-03-26 22:39 0前几天日常上线发布后,收到大量的CMS GC ... -
查看java对象在内存中的布局
2014-03-20 22:39 13058接着上篇《一个对象占用多少字节?》中遇到的 ... -
一个对象占用多少字节?
2014-03-18 21:56 35099老早之前写过一篇博客,是关于一个Integ ... -
cpu字长、操作系统字长和jvm中各数据类型占用的字节数关系
2014-03-16 02:05 4709cpu字长是指cpu同时参与运算的二进制位 ... -
cache line对内存访问的影响
2014-03-12 20:48 1374cache line对内存访问的影响很早就 ... -
一次线上问题的排查过程——时钟精度变化导致的cpu占用率高的问题
2013-09-16 21:14 4166最近升级了一次tair(缓存系统)的cli ... -
Kilim源码分析之五 ---- 织入之变量活跃性分析
2013-03-20 21:00 1212/** * In live va ... -
Kilim源码分析之四 ---- 织入之内联subroutine
2013-03-20 20:00 1491小于1.5编译级别时,如果不显示inlin ... -
Kilim源码分析之三 ---- 织入之构造/合并BasicBlock
2013-03-20 19:50 1431上一篇分析 ... -
在编译级别1.4时jvm编译try/catch/finally块的方式
2013-03-12 21:22 1472先上一段很简单,且不考虑健壮性的源码: ... -
ASM4.0源码走读之三 readCode方法分析方法代码
2013-03-09 00:19 1776继第一篇,我们来看看readCode的代码 ... -
ASM4.0源码走读之二 指令的类型
2013-03-08 23:51 1687在深入分析ClassReader.read ... -
ASM4.0源码走读之一
2013-03-08 23:18 1813了解java class ... -
java协程框架----kilim实现机制解析
2013-03-08 16:14 6622java语言处理多任务的模式是基于多线程,java语言级 ... -
Kilim源码分析之二 ---- 织入入口及可织入判断
2013-03-20 19:33 31211、织入入口,配置 1.1、织入入口 ... -
由一个小程序引发的思考 — 关于字段和方法的分派
2011-11-05 14:32 1839面向对象三大特征封装 ... -
JVM学习笔记十四 之 线程模型和锁
2011-10-24 02:30 0os线程模型、jvm线程、java线程调度、状态 线程安全程 ...
相关推荐
4. **减少内存泄漏**:避免创建过多的短生命周期对象,尤其是大数据量的集合,这可能会导致新生代频繁触发垃圾收集。 5. **代码优化**:良好的编程习惯可以降低GC压力,如及时释放不再使用的对象引用,避免使用静态...
这个资料包不仅涵盖了理论知识,还包含个人的学习笔记,对于学习和掌握JVM的各个方面都将大有裨益。无论是初学者还是经验丰富的开发者,都可以从中找到提升自己技能的宝贵资源。通过深入学习和实践,可以更好地理解...
### JVM学习笔记核心知识点整理 #### 一、引言与背景 随着软件开发技术的不断发展,Java作为一种广泛应用的编程语言,其背后的核心技术——Java虚拟机(JVM)的重要性日益凸显。掌握JVM不仅可以帮助开发者更好地理解...
这份资料出自B站上的【狂神说Java】系列教程,为快速入门JVM提供了详实的笔记。以下是根据这些资源可能包含的一些关键知识点的详细解析: 1. **JVM概述**: - JVM是Java平台的核心组成部分,它是一个运行Java字节...
新生代和老年代的概念用于区分对象的生命周期,不同的垃圾收集器如Serial、Parallel、CMS、G1等适用于不同场景。 5. **字节码执行**:JVM通过解释器将字节码转换为机器码执行,为了提高性能,JVM还实现了Just-In-...
栈帧的生命周期与方法同步,线程私有。 - **本地方法栈**:与虚拟机栈相似,但服务于本地(Native)方法。 - **堆**:所有线程共享,存储对象实例和数组。堆被分为新生代和老年代,新生代用于短期对象,老年代用于...
了解这个过程有助于我们理解和控制类的生命周期。 四、垃圾收集与内存优化 JVM的垃圾收集机制负责自动回收不再使用的对象所占用的内存,主要有标记-清除、复制、标记-整理和分代收集等算法。理解垃圾收集的工作...
### JVM学习笔记知识点详解 #### 一、JVM的基本结构 **JVM(Java Virtual Machine,Java虚拟机)**是一种可以执行Java字节码的虚拟机。它为Java提供了平台无关性,使得Java代码可以在任何安装了JVM的平台上运行。 ...
### 深入Java虚拟机JVM类加载学习笔记 #### 一、Classloader机制解析 在Java虚拟机(JVM)中,类加载器(ClassLoader)是负责将类的`.class`文件加载到内存中的重要组件。理解类加载器的工作原理对于深入掌握JVM以及...
JVM还提供了垃圾回收机制,自动管理对象的生命周期,避免了手动内存管理可能引发的内存泄漏和悬挂指针等问题。 #### 线程管理 在分布式应用中,线程管理尤为重要。JVM支持多线程执行,能够高效地处理并发任务。每...
- **分代收集(Generational GC)**:根据对象的生命周期,将堆分为新生代和老年代,不同年代采用不同的垃圾回收策略。 3. **垃圾回收器**: - **Serial GC**:单线程的垃圾回收器,适合轻量级应用。 - **...
新生代又细分为Eden区、Survivor区(From和To),用于进行垃圾收集和对象生命周期管理。 2. **方法区(Method Area)**:在Java 8之前,也被称为永久代,存储了类的信息,如类名、方法信息、常量池等。Java 8之后,...
理解这些区域的用途、生命周期以及它们之间的交互关系是JVM优化的重要一环。 3. **垃圾回收机制**:Java的自动内存管理依赖于垃圾回收器,包括新生代、老年代的划分,Minor GC和Full GC的区别,以及如何调优GC参数...
例如,如果发现年轻代的垃圾回收(YGC)次数频繁,可能意味着对象生命周期短,需要增大年轻代空间以减少对象晋升到老年代的几率;而老年代空间使用率过高则可能预示着内存溢出的风险,需要扩大老年代容量或者优化...
* 优化代码:使用 null 显式赋值、虚引用等方式及时回收大对象,减少大对象的生命周期,检查数据结构使用是否合理等。 4. JVM 对象创建 JVM 对象创建过程包括: * 类加载:类加载完毕后,其对象所需内存大小是...
《深入理解Java虚拟机:JVM高级特性与最佳实践(第3版)周志明.pdf》这本书是Java开发者深入理解JVM(Java Virtual ...而“新建文本文档.txt”可能包含个人笔记或者临时记录,对于JVM学习的具体内容没有直接关系。
在JVM生命周期方面,从启动一个Java程序到程序运行过程中,JVM实例与独立运行的Java程序相对应,具有进程级别的生命周期。 面向对象的知识点涉及UML表示法和设计模式。UML表示法小结中包括类图、顺序图、活动图和...
1. 对象生命周期:当一个对象被创建后,它会在Java堆内存中占据一定的空间。如果对象不再被程序引用,那么这个对象就变成了垃圾。 2. 垃圾回收器:Java虚拟机(JVM)包含一个或多个垃圾回收器,它们负责监测和回收...
3. **对象生命周期**:了解对象从创建到消亡的过程,包括新生代、老年代的划分,以及晋升机制,有助于优化对象分配和内存分配策略。 4. **内存泄漏**:识别和解决内存泄漏问题对性能优化至关重要。学习如何使用...