`

JVM内存模型以及垃圾收集策略解析

阅读更多
来自:http://yuquan-nana.iteye.com/blog/599750

一 JVM 内 存模型

1.1 Java

Java栈 是与每一个线程关联的, JVM 在 创建每一个线程的时候,会分配一定的栈空间给线程。它主要用来存储线程执行过程中的局部变量,方法的返回值,以及方法调用上下文。栈空间随着线程的终止而 释放。

StackOverflowError: 如果在线程执行的过程中,栈空间不够用,那么 JVM 就 会抛出此异常,这种情况一般是死递归造成的。

1.2 

Java中 堆是由所有的线程共享的一块内存区域,堆用来保存各种 JAVA 对象,比如数组,线程对象等。

1.2.1 Generation

JVM堆 一般又可以分为以下三部分:

Ø  Perm

Perm代 主要保存 class,method,filed 对 象,这部门的空间一般不会溢出,除非一次性加载了很多的类,不过在涉及到热部署的应用服务器的时候,有时候会遇到 java.lang.OutOfMemoryError : PermGen space  的 错误,造成这个错误的很大原因就有可能是每次都重新部署,但是重新部署后,类的 class 没有被卸载掉,这样就造成了大量的 class 对象保存在了 perm 中,这种情况下,一般重新启动应用 服务器可以解决问题。

Ø  Tenured

Tenured区主要保存生命周期长的对象,一般是一些老的对象,当一些对象在 Young 复制转移一定的次数以后,对象就 会被转移到 Tenured 区, 一般如果系统中用了 application 级 别的缓存,缓存中的对象往往会被转移到这一区间。

Ø  Young

Young区 被划分为三部分, Eden 区 和两个大小严格相同的 Survivor 区, 其中 Survivor 区 间中,某一时刻只有其中一个是被使用的,另外一个留做垃圾收集时复制对象用,在 Young 区间变满的时候, minor GC 就会将存活的对象移到空闲的 Survivor 区间中,根据 JVM 的策略,在经过几次垃圾收集后,任然 存活于 Survivor 的 对象将被移动到 Tenured 区 间。

1.2.2 Sizing the Generations

JVM提 供了相应的参数来对内存大小进行配置。

正如上面描述,JVM 中堆被分为了 3 个大的区间,同时 JVM 也提供了一些选项对 Young,Tenured 的大小进行控 制。

Ø  Total Heap 

-Xms : 指定了 JVM 初始 启动以后初始化内存

-Xmx: 指定 JVM 堆得最 大内存,在 JVM 启 动以后,会分配 -Xmx 参 数指定大小的内存给 JVM , 但是不一定全部使用, JVM 会 根据 -Xms 参数 来调节真正用于 JVM 的 内存

-Xmx -Xms之 差就是三个 Virtual 空 间的大小

Ø  Young Generation

-XX:NewRatio=8意 味着 tenured  和  young 的比值 8 1 ,这样 eden+2*survivor=1/9

堆内存

-XX:SurvivorRatio=32意 味着 eden 和一 个 survivor 的 比值是 32 1 ,这样一个 Survivor 就占 Young 区的 1/34.

-Xmn 参 数设置了年轻代的大小

Ø  Perm Generation

-XX:PermSize= 16 M -XX:MaxPermSize= 64 M

Thread  Stack

-XX:Xss=128K

 

1.3 堆栈分离的好处

 

呵呵,其它的先不 说了,就来说说面向对象的设计吧,当然除了面向对象的设计带来的维护性,复用性和扩展性方面的好处外,我们看看面向对象如何巧妙的利用了堆栈分离。如果从 JAVA内存模型的角度去理解面向对象的设计,我们就会发现对象它完美的表示了堆和栈,对象的数据放在堆中,而我们编写的那些方法一般都是运行在栈中,因 此面向对象的设计是一种非常完美的设计方式,它完美的统一了数据存储和运行。

 

二 JAVA 垃 圾收集器

2.1 垃圾收集简史

垃圾收集提供了内 存管理的机制,使得应用程序不需要在关注内存如何释放,内存用完后,垃圾收集会进行收集,这样就减轻了因为人为的管理内存而造成的错误,比如在C++ 语言里,出现内存泄露时很常见的。

Java语 言是目前使用最多的依赖于垃圾收集器的语言,但是垃圾收集器策略从 20 世纪 60 年代就已经流行起来了,比如 Smalltalk,Eiffel 等编程语言也集成了垃圾收集器的机制。

2.2 常见的垃圾收集策略

所有的垃圾收集算 法都面临同一个问题,那就是找出应用程序不可到达的内存块,将其释放,这里面得不可到达主要是指应用程序已经没有内存块的引用了,而在JAVA 中,某个对象对应用程序是可到达的是 指:这个对象被根(根主要是指类的静态变量,或者活跃在所有线程栈的对象的引用)引用或者对象被另一个可到达的对象引用。

2.2.1 Reference Counting(引用计数)

 引用计数是最简 单直接的一种方式,这种方式在每一个对象中增加一个引用的计数,这个计数代表当前程序有多少个引用引用了此对象,如果此对象的引用计数变为0 ,那么此对象就可以作为垃圾收集器的目标对 象来收集。

优点:

简单,直接,不需 要暂停整个应用

缺点:

1.需 要编译器的配合,编译器要生成特殊的指令来进行引用计数的操作,比如每次将对象赋值给新的引用,或者者对象的引用超出了作用域等。

2.不 能处理循环引用的问题

2.2.2 跟踪收集器

跟踪收集器首先要 暂停整个应用程序,然后开始从根对象扫描整个堆,判断扫描的对象是否有对象引用,这里面有三个问题需要搞清楚:

1. 如果每次扫描整个堆,那么势必让GC 的时间变长,从而影响了应用本身的执 行。因此在 JVM 里 面采用了分代收集,在新生代收集的时候 minor gc 只 需要扫描新生代,而不需要扫描老生代。

2. JVM采用了分代收集以后, minor gc 只扫描新生代,但是 minor gc 怎么判断是否有老生代的对 象引用了新生代的对象, JVM 采 用了卡片标记的策略,卡片标记将老生代分成了一块一块的,划分以后的每一个块就叫做一个卡片, JVM 采用卡表维护了每一个块的状态,当 JAVA 程序运行的时候,如果发现老生代对 象引用或者释放了新生代对象的引用,那么就 JVM 就 将卡表的状态设置为脏状态,这样每次 minor gc 的 时候就会只扫描被标记为脏状态的卡片,而不需要扫描整个堆。具体如下图:

3. GC在收集一个对象的时候会判 断是否有引用指向对象,在 JAVA 中 的引用主要有四种: Strong reference,Soft reference,Weak reference,Phantom reference.

Ø  Strong Reference 

强引用是JAVA 中默认采用的一种方式,我们平时创建的引用都属于强引用。如果一个对象没有强引用,那么对象就会被回 收。

public void testStrongReference(){

Object referent = new Object();

Object strongReference = referent;

referent = null;

System.gc();

assertNotNull(strongReference);

}

 

    

Ø  Soft Reference

软引用的对象在GC 的时候不会被回收,只有当内存不够用的时候才会真正的回收,因此软引用适合缓存的场合,这样使得缓存中的 对象可以尽量的再内存中待长久一点。

Public void testSoftReference(){

String  str =  "test";

SoftReference<String> softreference = new SoftReference<String>(str);

str=null;

System.gc();

assertNotNull(softreference.get());

}

 

Ø  Weak reference

弱引用有利于对象更快的被回收,假如一个对象没有强引用只有弱引用,那么在GC 后,这个对象肯定会被回收。

Public void test Weak Reference(){

String  str =  "test";

Weak Reference<String>  weakR eference = new  Weak Reference<String>(str);

str=null;

System.gc();

assertNull( weakR eference.get());

}

 

Ø  Phantom reference 

2.2.2.1 Mark-Sweep Collector(标记 - 清除收集器)

标记清除收集器最 早由Lisp 的发明人于 1960 年提出,标记清除收集器停止所有的 工作,从根扫描每个活跃的对象,然后标记扫描过的对象,标记完成以后,清除那些没有被标记的对象。

优点:

解决循环引用的问题

不需要编译器的配合,从而就不执行额外的指令

缺点:

1. 每个活跃的对象都要进行扫描,收集暂停的时 间比较长。

2.2.2.2 Copying Collector(复制收集器)

复制收集器将内存 分为两块一样大小空间,某一个时刻,只有一个空间处于活跃的状态,当活跃的空间满的时候,GC 就会将活跃的对象复制到未使用的空间中去,原来不活跃的空间就变为了活跃的空间。

复制收集器具体过 程可以参考下图:

优点:

只 扫描可以到达的对象,不需要扫描所有的对象,从而减少了应用暂停的时间

缺点:

1. 需要额外的空间消耗,某一个时刻,总是有一 块内存处于未使用状态

2. 复制对象需要一定的开销

2.2.2.3 Mark-Compact Collector(标记 - 整理收集器)

标记整理收集器汲 取了标记清除和复制收集器的优点,它分两个阶段执行,在第一个阶段,首先扫描所有活跃的对象,并标记所有活跃的对象,第二个阶段首先清除未标记的对象,然 后将活跃的的对象复制到堆得底部。标记整理收集器的过程示意图请参考下图:

 

Mark-compact策 略极大的减少了内存碎片,并且不需要像 Copy Collector 一 样需要两倍的空间。

2.3 JVM的垃圾收集策略

   GC的执行时要耗费一定的 CPU 资源和时间的,因此在 JDK1.2 以后, JVM 引入了分代收集的策略,其中对新生代 采用 "Mark-Compact" 策 略,而对老生代采用了“ Mark-Sweep" 的 策略。其中新生代的垃圾收集器命名为“ minor gc ”, 老生代的 GC 命名 为 "Full Gc  或 者 Major GC". 其 中用 System.gc() 强 制执行的是 Full Gc.

2.3.1 Serial  Collector

Serial Collector 是指任何时刻都只有一个线程进行垃圾收集, 这种策略有一个名字“stop the whole world", 它 需要停止整个应用的执行。这种类型的收集器适合于单 CPU 的机器。

Serial Copying Collector

此种GC -XX:UseSerialGC 选项配置,它只用于 新生代 对象的收集。1.5.0 以后 .

-XX:M axTenuringThreshold来设置对象复制的次数。当 eden 空间不够的时候, GC 会将 eden 的活跃对象和一个名叫 From survivor 空间中尚不够资 格放入 Old 代的 对象复制到另外一个名字叫 To Survivor 的 空间。而此参数就是用来说明到底 From survivor 中 的哪些对象不够资格,假如这个参数设置为 31 , 那么也就是说只有对象复制 31 次 以后才算是有资格的对象。

这里需要注意几个个问题:

Ø   From Survivor To survivor 的角色是不断的变化的,同一时间只有一块空间处于使用状态,这个 空间就叫做 From Survivor 区, 当复制一次后角色就发生了变化。

Ø   如果复制的过程中发现To survivor 空 间已经满了,那么就直接复制到 old generation.

Ø   比较大的对象也会直接复制到Old generation, 在 开发中,我们应该尽量避免这种情况的发生。

Serial  Mark-Compact Collector

串行的标记- 整 理收集器是 JDK5 update6 之 前默认的老生代的垃圾收集器,此收集使得内存碎片最少化,但是它需要暂停的时间比较长

2.3.2 Parallel Collector 

Parallel Collector主 要是为了应对多 CPU , 大数据量的环境。

Parallel Collector又 可以分为以下两种:

Parallel Copying Collector

此种GC -XX:UseParNewGC 参数配置, 它主要用于 新生代 的收集, GC 可以配合 CMS 一起使用。 1.4.1 以后

Parallel Mark-Compact Collector

此种GC -XX:U seParallelOldGC 参数配置,此GC 主要用于 老生代 对象的收集。1.6.0

Parallel  scavenging  Collector

此种GC -XX:UseParallelGC 参数配置,它是对 新生代 对象的垃圾收集器,但是它不能和CMS 配 合使用,它适合于比较大新生代的情况,此收集器起始于 jdk 1.4.0 。它比较适合于对吞吐量高于暂停时间的场合。

Serial gc Parallel gc 可以用如下的图来表 示:

2.3.3 Concurrent Collector

C oncurrent Collector通 过并行的方式进行垃圾收集,这样就减少了垃圾收集器收集一次的时间,这种 GC 在实时性要求高于吞吐量的时候比较有用。

此种GC 可以用参数 -XX:U seConcMarkSweepGC 配置,此GC 主要用于 老生代 Perm 代 的收集。

 

参考资料

1 http://developers.sun.com/mobility/midp/articles/garbage/

2 http://developers.sun.com/mobility/midp/articles/garbagecollection2/

3 http://blogs.sun.com/watt/resource/jvm-options-list.html

4 http://java.sun.com/developer/technicalArticles/Programming/turbo/

5 http://www.ibm.com/developerworks/library/j-jtp10283/index.html?S_TACT=105AGX52&S_CMP=cn-a-j

6 http://www.ibm.com/developerworks/library/j-jtp11253/index.html?S_TACT=105AGX52&S_CMP=cn-a-j

分享到:
评论

相关推荐

    JVM内存模型及垃圾收集策略解析

    **JVM内存模型及垃圾收集策略解析** Java虚拟机(JVM)是Java程序运行的基础,它为Java程序提供了一个跨平台的运行环境。在深入理解JVM内存模型与垃圾收集策略之前,我们首先需要知道JVM的主要组成部分:类装载器、...

    jvm内存模型以及垃圾回收机制.pptx

    Java虚拟机(JVM)内存模型...理解JVM内存模型和垃圾回收机制对于优化Java应用性能、避免内存泄漏和有效利用资源至关重要。开发者应根据实际需求选择合适的垃圾回收器,并关注内存分配策略,以实现高效稳定的程序运行。

    JVM内存模型

    HotSpot VM在Java 5中引入了一系列自适应调优特性,能够在JVM启动时根据底层平台和系统配置自动选择合适的垃圾收集器、配置Java堆大小以及选择运行时JIT编译器。例如,在Server模式下,初始堆或堆的最小值(`-Xms`)...

    JVM内存模型(通俗易懂)

    JVM内存模型则是理解Java程序性能优化的关键,因为它规定了程序中的数据如何在内存中分配和访问。本文将深入浅出地探讨JVM内存模型,帮助你快速掌握这一核心概念。 首先,我们要知道JVM内存模型主要分为以下几个...

    jvm相关代码仓库,包括类加载机制,字节码执行机制,JVM内存模型,GC垃圾回收,JV-jvm_practice.zip

    3. **JVM内存模型**: JVM内存主要分为堆内存(Heap)、栈内存(Stack)、方法区(Method Area)、程序计数器(PC Register)和本地方法栈(Native Method Stack)。重点理解对象创建、内存分配以及栈帧的工作方式...

    java -jvm 内存分配和jvm调优

    新生代又细分为Eden区、From Survivor区和To Survivor区,用于垃圾收集策略。 2. 栈(Stack):每个线程都有一个独立的栈,用于存储方法调用时的局部变量、方法参数和返回地址。当方法执行完毕,栈帧会出栈。 3. ...

    java中jvm内存分配相关资料总结整理

    垃圾收集器会定期检查不再使用的对象并回收其占用的空间。 3. **垃圾回收** - **GC(Garbage Collection)**:Java的自动内存管理机制,负责清理无用对象,避免内存泄漏。 - **GC算法**:常见的有标记-清除、复制...

    介绍JVM和Java内存模型的两篇非常不错的资料

    优化JVM内存配置是提升Java应用性能的重要手段,例如调整堆大小、新生代与老年代的比例、设置并发垃圾收集策略等。理解JVM的工作原理和内存模型,可以帮助我们更有效地诊断和解决内存相关的问题,比如内存溢出、内存...

    JVM深入解析(JVM specification 和Sun的JVM的内存机制)

    - 堆内存分为新生代(Young Generation)和老年代(Tenured Generation),新生代又分为Eden区和两个Survivor区,用于实现高效的垃圾收集策略,如Minor GC和Major GC。 - 堆栈分离设计有助于提高系统性能,避免...

    JVM常见面试题解析.pdf

    2. JVM内存模型:JVM内存分为五个区域:程序计数器、虚拟机栈、本地方法栈、Java堆和方法区。程序计数器记录当前线程执行的字节码指令地址;虚拟机栈和本地方法栈分别服务于Java方法和本地(Native)方法;Java堆是...

    JVM内存资料.zip_jdk

    《JVM内存模型深度探索——基于JDK源码解析》 在Java开发中,深入理解JVM(Java Virtual Machine)内存模型是至关重要的。本文将从JDK源码的角度出发,详细探讨JVM的内存结构、类加载机制以及对象创建与内存分配...

    深入浅出jvm虚拟机视频大全(jvm性能调优+内存模型+虚拟机原理)

    ### JVM内存模型详解 #### 1. 方法区 方法区主要存储类的信息、常量、静态变量等数据。这些数据通常在类加载时创建,并且在整个JVM生命周期中保持不变。对于HotSpot虚拟机而言,这部分区域被称为“永久代”(Perm...

    jdk1.8的jvm内存模型——实习生必须了解的

    【JDK 1.8 的 JVM 内存模型】 JVM(Java Virtual ...了解JVM内存模型和垃圾收集算法是Java开发者的基本功,对于实习生来说,掌握这些知识不仅能帮助应对面试,也能在实际开发中更好地理解和优化程序的内存使用。

    深入理解JVM&G1; GC

    总之,《深入理解JVM & G1 GC》这本书为读者提供了理解JVM内存模型和G1 GC的深入见解,有助于Java开发者更好地理解和控制JVM的内存管理,提升应用程序的稳定性和效率。通过学习这些知识,开发者可以解决实际开发中...

    JVM入门到JVM 调优实战

    总结来说,从JVM入门到实战调优,涉及了Java程序从编译到执行的全过程,包括Class文件格式、类加载、内存模型、运行时数据区、垃圾收集策略以及调优实践等多个层面。深入理解和掌握这些知识,不仅有助于提升Java程序...

    jvm paper jvm

    二、JVM内存模型 1. 堆(Heap):所有对象都在堆中分配内存,是线程共享的区域,分为新生代和老年代。 2. 新生代(Young Generation):新生代进一步划分为Eden区和两个Survivor区(From空间和To空间)。大部分对象...

    JVM Diagnostics Guide 1.4.2

    5. **性能调优**:涵盖JVM性能监控工具的使用,如JConsole、VisualVM等,以及如何通过调整JVM参数(如-Xms, -Xmx, -XX:NewRatio等)来优化内存分配和垃圾收集策略。 6. **线程分析**:讲解如何理解和诊断线程问题,...

    基于Java虚拟机内存模型的性能调优方法.zip

    总结来说,基于Java虚拟机内存模型的性能调优涉及到多个层面,包括理解内存结构、垃圾回收机制、内存分配策略、调优工具的使用以及JVM参数的设置。通过深入理解和实践这些知识,开发者能够有效地优化Java应用程序,...

    JVM 虚拟机原理、内存结构、优化、垃圾回收分析等全方位的讲解,专业!

    本文将深入探讨JVM的核心原理、内存结构、优化策略以及垃圾回收机制。 首先,JVM的内存结构是理解其工作方式的关键。在JVM中,内存主要分为五个区域:程序计数器、虚拟机栈、本地方法栈、堆和方法区(在Java 8之后...

Global site tag (gtag.js) - Google Analytics