JVM内存组成和GC机制

标签: JVM Java Java | 发表时间:2015-08-26 02:18 | 作者:
出处:http://mzorro.me/

按照官方的说法:

Java 虚拟机具有一个堆,堆是运行时数据区域,所有类实例和数组的内存均从此处分配。堆是在 Java 虚拟机启动时创建的。
在JVM中堆之外的内存称为非堆内存(Non-heap memory)。

可以看出JVM主要管理两种类型的内存: 非堆。简单来说堆就是Java代码可及的内存,是留给开发人员使用的;非堆就是JVM留给 自己用的,所有方法区、JVM内部处理或优化所需的内存(如JIT编译后的代码缓存)、每个类结构(如运行时常数池、字段和方法数据)以及方法和构造方法 的代码都在非堆内存中。

JVM内存组成

方法栈&本地方法栈

线程创建时产生,方法执行时生成栈帧。在Sun JDK中没有本地方法栈,只有每个线程单独的方法栈。此栈大小可以用-Xss来控制。

栈溢出会出现StackOverflowError异常,在Java中因为对象都是存在堆中,所以造成此错误一般是因为方法调用的递归深度太大(一般是产生了循环递归),可以根据输出的错误堆栈信息排查代码中的问题。
若在申请线程过多时出现OutOfMemeoryError,是因为无法为新的线程申请到栈内存,这时可以尝试降低堆内存(-Xms、-Xmx),从而给操作系统预留更多的内存。

方法区

方法区是JVM中 所有线程共享的内存区,存储 已被虚拟机加载的类信息常量静态变量、即时编译后的代码等数据。虽然Java虚拟机规范把方法区描述为堆的一个逻辑部分,但是它却有一个别名叫做 Non-Heap(非堆),目的应该是与Java的堆区区分开来。
对于习惯在HotSpot(Sun公司开发的)虚拟机上开发和部署程序的开发者来说,很多人更愿意将之称为 永久代(或 持久代Permanent Generation)。本质上两者并不等价,只是因为HotSpot虚拟机的设计团队把GC分代收集扩展至方法区,或者说使用永久代来实现方法区而已。
方法区的大小可以用-XX:PermSize(初始大小)和-XX:MaxPermSize(最大值)来控制。一般默认固定为64M。

当方法区出现溢出时(通常是因为加载了大量的类或常量),将会出现OutOfMemeoryError,并提示是PermGen space,此时可以用-XX:PermSize和-XX:MaxPermSize来增大方法区的大小。

java代码中所有的new操作都会在堆中申请内存,这是JVM运行时的主要内存区域。
JVM初始分配的内存由 -Xms指定,默认是物理内存的1/64;JVM最大分配的内存由 -Xmx指 定,默认是物理内存的1/4。默认空余堆内存小于40%时,JVM就会增大堆直到-Xmx的最大限制;空余堆内存大于70%时,JVM会减少堆直到-Xms的最小限制。因此服务器一般设置-Xms、-Xmx相等以避免在每次GC后调整堆的大小。

通过-XX:+HeapDumpOnOutOfMemoryError可以让虚拟机在出现内存溢出异常时Dump出当前的内存堆转储快照以便事后进行分析。出现heap space的OOM可能是因为内存溢出或者内存泄漏。如果是内存溢出,则需进一步使用工具分析泄漏对象,从而找到代码漏洞。如果是内存溢出,则需要根据物理内存调整-Xms与-Xmx的值。

堆内存分配和回收

根据GC分代收集策略,将堆内存分为 新生代(New Generation)与 老年代(Old Generation)。其中,新生代又分为Eden和两个Survivor。如下图所示:

新生代的大小可以用虚拟机参数-Xmn来设置,Sun官方推荐的配置为整个堆的3/8。也可以用-XX:NewRatio来设置新生代与老年代的比例(-XX:NewRatio=4,则新生代与年老代所占比值为1:4,新生代占整个堆栈的1/5)。

其中新生代的两个Survivor分别称为from space和to space,这两块空间交替使用,每次只使用了其中的一个,也就是说新生代中真正在使用的是Eden空间和一个Survivor空间。HotSpot虚拟机默认的Eden和Survivor的大小比例是8:1,也就是说每次新生代中可用的空间为总新生代容量的90%,还有10%是用于下次发生MinorGC(在下面会详细讲到)时收集还存活着的对象。可以使用-XX:SurvivorRatio来设置新生代中Eden区与Survivor区的大小比值,默认为8(例如-XX:SurvivorRatio=4,则两个Survivor区与一个Eden区的比值为2:4,一个Survivor区占整个新生代的1/6)。

对象优先在Eden分配

大多数情况下对象在新生代的Eden区中分配,当Eden中没有足够的空间时,虚拟机就会发起一次MinorGC。

MinorGC发起时,会将Eden和当前正在使用的Survivor空间中还存活着的对象一次性的复制到另一个Survivor空间中,然后将当前Eden和当前的Survivor清空,开始使用另一个Survivor。当然,如果Survivor的空间不够,从Eden和正在使用的Survivor中回收的对象无法存到Survivor中,则会将活着的对象交给老年代进行分配担保(Handle Promotion)。

大对象直接进入老年代

所谓大对象,就是需要大量连续内存空间的Java对象,最典型的就是那种很长的字符串及数组。当出现大对象时,会直接进入老年代。而如果老年代空间满,则会触发一次FullGC,与MinorGC相比,FullGC是一个非常重量级的操作。所以写程序时应该避免使用一些“朝生夕死”的“短命大对象”,这些大对象直接进入老年代而后就不再使用,从而容易触发FullGC,降低程序的性能。

虚拟机提供了一个-XX:PretenureSizeThreshold参数,另大于这个设置值的对象直接在脑年代中分配。这样做的原因是避免频繁的MinorGC中在Eden区和两个Survivor区之间发生大量耗时的内存拷贝。(这个参数必须以字节为单位,如-XX:PretenureSizeThreshold=3145728,表示3MB,而不能直接写3M)。

长期存活的对象进入老年代

当一个存活的对象在MinorGC中第一次被拷贝到Survivor中时,其年龄会被设置为1。此后,每次这个对象熬过一次MinorGC时,其年龄就会加1,默认情况下,当对象的年龄达到15时,将会被拷贝进老年代,从而减少MinorGC中Eden和两个Survivor之间的内存拷贝。

对象年龄的阈值可以用虚拟机参数-XX:MaxTenuringThreshold来设置,默认为1。

动态对象年龄判定

虚拟机并不是一定要等到对象的年龄达到MaxTenuringThreshold才将其移动到老年代。如果Survivor空间中相同年龄的所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代,无需等到MaxTenuringThreshold的年龄。

JVM的GC参数

参考

http://unixboy.iteye.com/blog/174173
http://www.cnblogs.com/redcreen/archive/2011/05/04/2036387.html

相关 [jvm 内存 gc] 推荐:

JVM内存组成和GC机制

- - Mz的博客
Java 虚拟机具有一个堆,堆是运行时数据区域,所有类实例和数组的内存均从此处分配. 堆是在 Java 虚拟机启动时创建的. 在JVM中堆之外的内存称为非堆内存(Non-heap memory). 可以看出JVM主要管理两种类型的内存: 堆和 非堆. 简单来说堆就是Java代码可及的内存,是留给开发人员使用的;非堆就是JVM留给 自己用的,所有方法区、JVM内部处理或优化所需的内存(如JIT编译后的代码缓存)、每个类结构(如运行时常数池、字段和方法数据)以及方法和构造方法 的代码都在非堆内存中.

【JVM】HotSpot JVM内存管理和GC策略总结

- - ITeye博客
JVM的相关知识是学习java高级特性必须要去深入学习的. 平时也有一些学习和实践,不过总结比较少. 今天有时间总结一下最基础的内存模型和GC策略的知识,在此记录一下. hotspot jvm内存模型. hotspot的内存模型很多地方都有类似总结,我也简单总结了一下,大概可以用下图表示:. 1.线程栈:线程创建是会为每个线程创建一个线程栈,线程栈里面会为每个方法调用创建一个栈帧.

JVM初探——使用堆外内存减少Full GC

- - ImportNew
GCIH可以联想到:  将长期存活的对象(如Local Cache)移入堆外内存(off-heap, 又名. 直接内存/direct-memory), 从而减少CMS管理的对象数量, 以降低Full GC的次数和频率, 达到提高系统响应速度的目的.. 这个idea最初来源于TaobaoJVM对OpenJDK定制开发的GCIH部分(详见 撒迦的分享- JVM定制改进@淘宝), 其中GCIH就是将CMS Old Heap区的一部分划分出来, 这部分内存虽然还在堆内, 但已不被GC所管理.

JVM初探- 内存分配、GC原理与垃圾收集器

- - IT瘾-geek
JVM初探- 内存分配、GC原理与垃圾收集器. JVM内存的分配与回收大致可分为如下4个步骤: 何时分配 -> 怎样分配 -> 何时回收 -> 怎样回收. new时分配外, 我们着重介绍后面的3个步骤:. 怎样分配- JVM内存分配策略. 对象内存主要分配在新生代 Eden区, 如果启用了本地线程分配缓冲, 则 优先在TLAB上分配, 少数情况能会直接分配在老年代, 或被拆分成标量类型在栈上分配(JIT优化).

JVM垃圾回收(GC)原理

- kill - yiihsia[互联网后端技术]_yiihsia[互联网后端技术]
引用计数(Reference Counting). 原理是此对象有一个引用,即增加一个计数,删除一个引用则减少一个计数. 垃圾回收时,只用收集计数为0的对象. 此算法最致命的是无法处理循环引用的问题. 标记-清除(Mark-Sweep). 第一阶段从引用根节点开始标记所有被引用的对象,第二阶段遍历整个堆,把未标记的对象清除.

JVM的GC简介和实例

- - 搜索技术博客-淘宝
本文是一次内部分享中总结了jvm gc的分类和一些实例, 内容是introduction级别的,供初学人士参考. 成文仓促,难免有些错误,如果有大牛发现,请留言,我一定及时更正,谢谢. JVM内存布局主要包含下面几个部分:. Java Virtual Machine Stack: 也就是我们常见的局部变量栈,线程私有,保存线程执行的局部变量表、操作栈、动态连接等.

如何查看GC 及jvm配置

- - 操作系统 - ITeye博客
java虽然是自动回收内存,但是应用程序,尤其服务器程序最好根据业务情况指明内存分配限制. 表示JVM Heap(堆内存)最小尺寸128MB,初始分配. 表示JVM Heap(堆内存)最大允许的尺寸256MB,按需分配. 说明:如果-Xmx不指定或者指定偏小,应用可能会导致java.lang.OutOfMemory错误,此错误来自JVM不是Throwable的,无法用try...catch捕捉.

JVM内存分配

- - 移动开发 - ITeye博客
计算机内存,它算是CPU与计算机打交道最频繁的区域,所有数据都是先经过硬盘至内存,然后由CPU再从内存中获取数据进行处理,又将数据保存到内存,通过分页或分片技术将内存中的数据再flush至硬盘. 那JVM的内存结构到底是如何呢. JVM做为一个运行在操作系统上,但又独立于os运行的平台,它的内存至少应该包括象寄存器、堆栈等区域.

FULL GC有可能导致JVM暂停1分钟以上吗?

- - 高级语言虚拟机
作者: qianhd . 链接: http://hllvm.group.iteye.com/group/topic/28745 . 发表时间: 2011年12月30日. 声明:本文系ITeye网站发布的原创文章,未经作者书面许可,严禁任何网站转载本文,否则必将追究法律责任. 与另外个程序交互有个心跳检测, 15s/次, 31s没有收到心跳检测的返回消息就认为连接断了,.

jvm对象分配及GC代码实例

- - Java - 编程语言 - ITeye博客
虚拟机采用的是HotSpot内核.   1.对象优先分配在Eden区,如果Eden区没有足够的空间时,虚拟机执行一次Minor GC.   2.大对象直接进入老年代(大对象是指需要大量连续内存空间的对象). 这样做的目的是避免在Eden区和两个Survivor区之间发生大量的内存拷贝. 通过参数-XX:PretenureSizeThreshold=3145728控制.