您的位置:首页 > 编程语言 > Java开发

Java HotSpot JVM内存管理之详解

2014-05-06 11:56 337 查看
关键字:HotSpot JVM(Java Virtual Machine) 内存管理(Memor Management)

本文主要参考Memory Management in the Java HotSpot Virtual MachineInside the Java Virtual MachineJVM说明书,力求准确地描述JVM内存管理机制,以及常见的内存相关的错误处理方式。如果发现本文有不正确或者不准确的地方,麻烦大侠不吝赐教。

JVM的种类有很多,比如Oracle HotSpotOracle JRockitIBM J9,以及其它更多,它们都实现了Java虚拟机规范,但内存管理机制的实现方式各异,本文主要描述的是Oracle HotSpot,也就是我们从Java SE Downloads下载安装的JVM。

JVM的内存管理是对运行时数据区(Runtime Data Area)的管理,其中主要是对堆(Heap)的管理。本文会简要介绍运行时数据区各个组件的作用,然后介绍HotSpot JVM对内存的管理,以及HotSpot常见内存问题的解决方式。

JVM的运行时数据区

如下图所示,JVM的运行时数据区由5部分组成[1]:方法区(Method Area),堆(Heap),栈(Java Stackes),pc寄存器和本地方法栈(Native Method Stacks)。本节简要介绍下每个数据区的作用。



方法区:类加载器子系统将Java类加载到JVM中后,类的信息会保存在方法区,比如类的静态成员,构造器,方法等。在虚拟机启动时方法区就会被创建。java.lang.OutOfMemoryError: PermGen异常就是由于该区空间耗尽而引起的,方法区由多个线程共享。

:用于存放运行时创建的对象。在虚拟机启动时方法区就会被创建。java.lang.OutOfMemoryError: Java heap space异常就是由于该区空间耗尽而引起的。堆由多个线程共享。

注意:虽然JVM说明书中有规定方法区和堆是由多个线程共享的,但值得注意的是,在HotSpot JVM中,是允许各个线程在堆中拥有自己的私有区域的(EDEN区,设置Thread Local Allocation Buffer),本文后面会有详述。



PC寄存器(Program Counter Register):JVM中运行的每个线程都有自己的PC寄存器,当该线程执行的方法是本地方法时,PC寄存器保存的值为undefined;否则保存的是JVM内当前正在执行的指令的地址。

:当一个线程创建时,JVM会同时为它创建私有的栈,它以帧(Frame)为单位保存局部变量,中间结果等,在方法调用和结果返回时发挥作用。对栈的操作只有压栈和出栈。这个区域的空间耗尽会引起java.lang.StackOverflowError。如果栈的大小是可以变化的,它也可能会引起java.lang.OutOfMemoryError。

本地方法栈(Native Method Stacks):如果虚拟机支持本地方法调用,再创建线程时,应该给线程创建本地方法栈。与栈相同,这个区域的空间耗尽会引起java.lang.StackOverflowError。如果空间大小是可以变化的,它也可能会引起java.lang.OutOfMemoryError。



HotSpot内存划分

基于以上JVM说明书对运行时数据区组件的定义,在J2SE 5.0u6之后,HotSpot JVM的内存区域大致如下划分:



其中绿色区域是垃圾回收器(Garbage Collector)会处理的区域。HotSpot把堆分为新生代和老年代,在J2SE 5.0u6之后,JVM自带的四种垃圾回收器都是基于按代划分的内存管理的,其理论依据是弱代假设(Weak Generational Hypothesis):

大多数对象的生命期都很短;

从年老对象到年轻对象的引用非常少。

所以可以根据对象的生命期不同,采用不同的策略进行垃圾回收,使垃圾回收更高效。其中,年轻代又细化为如下区域:



在后续博文中会详述垃圾回收器是如何基于这种划分方式进行垃圾回收的。

当一个对象新创建后,会放在年轻代,具体地说,是Eden区,经过几次垃圾回收后,如果对象仍然存活,则会将它放入年老区。

在多线程的环境下,多个线程可能同时创建新对象,并且都会放入Eden区,为了线程同步,Eden区只被一个线程使用。在多核的计算机中,为了减缓由于这种竞争导致的CPU的等待,HotSpot允许每个线程在Eden区拥有自己的私有区域,通过设置TLAB(Thread Local Allocation Buffer)。相关参数:

-XX:+UseTLAB

-XX:TLABSize=<size in kb>

-XX:+ResizeTLAB

-XX:+PrintTLAB

HotSpot常见内存问题的可能原因及解决方式

本节将描述HotSpot常见内存问题的可能原因,以及使用JVM参数进行调整的方式。

java.lang.OutOfMemoryError: Java heap space

原因可能有三:

程序存在内存泄露,即,长生命期的对象对短生命期的对象进行的引用,使得短生命期的对象无法被垃圾回收器回收;

类过度重载了finalize方法,使得大量对象因需要执行该方法而无法回收;

JVM的堆太小,可能由于硬件原因,也可能是配置原因。可尝试用如下参数调整:

-Xmx, -Xms

注意:一般遇到OutOfMemory错误就调整-Xmx, -Xms配置,这是不对的,这两个参数仅仅控制Heap区,不包括Permanent Generation及其它区域。

注意:-Xmn用于配置年轻代在Heap区允许占有的空间,是一个固定值。它能带来的好处是减少了垃圾回收器调整年轻代的大小开销,但它可能导致在内存足够使用的情况下由于限制了年轻代的大小,而导致OutOfMemory错误,一般推荐使用-XX:NewRatio=<value>。

java.lang.OutOfMemoryError:PermGen space

可能原因:

Permanent Generation空间耗尽,类加载器无法加载类到JVM中。可使用如下参数调整:

调整初始值:-XX:PermSize=<value>

调整最大值:-XX:MaxPermSize=<value>

java.lang.OutOfMemoryError:Requested array size exceeds VM limit

可能原因:

程序错误以致需要创建非常大的数组;

JVM的Heap区太小,可调整-Xmx, -Xms配置

java.lang.OutOfMemoryError:<reason> <stack trace> (Native method)

可能原因:

本地方法调用时出现了内存分配错误。

java.lang.OutOfMemoryError:unable to create new native thread

可能原因:

缺少内存空间创建新线程,可能的解决方式:

减少Heap的大小–Xmx,留出更多空间给线程栈

减小每个线程使用的栈空间–Xss

注意:–Xss太小可能导致java.lang.StackOverflowError。

转自: /article/2471592.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: