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

浅谈Java内存及内存溢出

2016-08-31 00:00 246 查看
摘要: Java内存及内存溢出知识的总结

最近有点时间,看了一下《深入理解Java虚拟机》一书,觉得需要总结一些东西。话不多说了开始了.

Java运行时数据区域:

Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域。如下图



首先,说一下那些内存区域是线程私有的:虚拟机栈(VM Stack)、本地方法栈(Native Method Stack)

程序计数器(Program Counter Register)。共有的:方法区(Method Area)、堆(Heap)

程序计数器(Program Counter Register):

程序计数器(Program Counter Register)是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器。

虚拟机栈(VM Stack):

虚拟机栈(VM Stack)是线程私有的,它的生命周期与线程相同。虚拟机栈描述的是Java方法执行的内存模型:每个方法在执行时都会创建一个栈帧(Stack Frame),主要用来存放 局部变量、操作数栈、动态链接、方法出口等消息。

局部变量表存放:基本数据类型(boolean byte char short int float long double )、对象引用(reference类型,记住 它不等同于对象本身,可能是一个指向对象起始地址的引用指针,也可能是指向一个代表对象的句柄或其他与此对象相关的位置)和returnAddress类型(指向了一条字节码指令的地址)

本地方法栈(Native Method Stack)

本地方法栈(Native Method Stack)与虚拟机栈所发挥的作用是非常相似的,它们之间的区别不过是虚拟机栈为虚拟机指向Java方法(也就是字节码)服务,而本地方法栈则为虚拟机使用到的Native方法服务。

Java堆:

Java堆(Java Heap)是Java虚拟机所管理的内存中最大的一块。Java堆是被所有线程共享的一块内存区域。此区域的唯一目的就是存放对象实例。几乎所有的对象实例都在这里分配内存。所有的对象实例以及数组都要在堆上分配。

方法区:

方法区(Method Area)与Java堆一样,是各个线程共享的内存区域。它主要存储已被虚拟机加载的 类信息、常量、静态变量、即使编译器编译后的代码等数据。虽然Java虚拟机规范把方法区描述为堆得一个逻辑部分,但是它有个别名叫做Non-Heap(非堆),目的应该是与Java堆区分。其实很多人都愿意把方法区成为“永久代(Permanent Generation)”本质上两者并不等价,更详细请参考《深入理解Java虚拟机》。

运行时常量池:是方法区的一部分,Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息室常量池(Constant Pool Table),用于存放编译期生成的各种字面量和符号引用,这部分内容将在类加载后进入方法区的运行时常量池中存放。

直接内存:

直接内存(Direct Memory)并不是虚拟机运行时数据区的一部分。也不是Java虚拟机规范中定义的内存区域。但是这部分内存也是被频繁德使用。jdl1.4中加入了NIO类,引入了一种基于通道(Channel)与缓冲区(Buffer)的I/O方式,它可以使用Native函数库直接分配堆外内存,然后通过一个存储在Java堆中的DirectByteBuffer对象作为这一块内存的引用进行操作。这样能在一些场景中显著提高性能,因为避免了在Java堆和Native堆中来回复制数据。

说完了以上各个内存区域的作用,(以上各个内存区域的作用大部分出自于《深入理解Java虚拟机》中),下面更通俗的说分类说明以下,我们经常有人把Java内存分为堆内存(Heap)和栈内存(Stack)。其中栈就是上面说的虚拟机栈。

堆(Heap)区:新生代、老生代

非堆(haep )区:代码缓存区(CodeCache)、PermGen(永久代及方法区)、Java虚拟机栈、本地方法栈(Native Method Stack)。

Java堆(Heap):新生代:Eden空间、From Survivor空间、To Survivor空间

老年代(Tenured Generation)

方法区:又称非堆,永久代(Permanent Generation)(此处和上面非堆区有些冲突。下面是出自《深入理解Java虚拟机》)

介绍完上面,下面说一下OOM(OutOfMemory)的事:

首先说一下可能出现OOM的内存区域:

1.抛出:StackOverflowError

Java虚拟机栈、本地方法栈

2.抛出:OutOfMemoryError

Java虚拟机栈、本地方法栈、Java堆(Heap)、方法区(Method Area) 运行时常量池、直接内存

Java虚拟机栈:

会出现两种异常状况:StackOverflowError:如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常;OutOfMemoryError:如果虚拟机栈可以动态扩展(当前大部分的Java虚拟机都可以动态扩展,只不过Java虚拟机规范中也允许固定长度的虚拟机栈),如果扩展是无法申请到足够的内存,就会抛出OutOfMemoryError异常。

控制参数:栈容量只有-Xss参数设定.

本地方法栈(Native Method Stack):

与虚拟机栈一样,本地方法栈(Native Method Stack)区域也会抛出StackOverflowError和OutOfMemoryError异常。

控制参数:栈容量只有-Xoss参数设定,注:由于在HotSpot虚拟机中并不区分虚拟机栈和本地方法栈,因此,对于HotSpot来说,虽然-Xoss参数(设置本地方法栈大小)存在,但实际上是无效的,栈容量只有-Xss参数设定)

Java堆:

如果堆中没有内存完成实例分配,并且对也无法再扩展时,将会抛出OutOfMemoryError异常,

控制参数:-Xmx和-Xms控制。

方法区(Method)和运行时常量池:

和Java堆一样,当方法区无法满足内存分配需要时,将抛出OutOfMemoryError异常。

控制参数:-XX:PermSize和-XX:MaxPermSize限制方法区大小

直接内存:

如果忽略直接内存,使得各个内存区域总和大于物理内存限制(包括物理的和操作系统级的限制),从而导致动态扩展是出现OutOfMemoryError异常。

控制参数:-XX:MaxDirectMemorySize制定,如果不指定,则默认与Java堆最大值(-Xmx制定)一样。

以下是Java内存参数详细说明:以下内存摘自

1. 设置JVM内存的参数有四个:

a) 堆内存设置:程序可以到达的,可以操作的

-Xmx 最大堆内存分配 默认物理内存1/4,当空余堆内存大于70%时,会减小到-Xms的最小限制。 一般设置 -Xms和Xms大小相等,最佳设值应该视物理内存大小及计算机内其他内存开销而定;

-Xms 初始堆内存 默认物理内存1/64,也是最小分配堆内存。当空余堆内存小于40%时,会增加到-Xms的最大限制 ,Server端JVM最好将-Xms和-Xmx设为相同值,开发测试机JVM可以保留默认值;

-Xmn Java Heap Young区大小,不熟悉最好保留默认值;

-Xss 每个线程的Stack大小,不熟悉最好保留默认值;

b) 非堆内存设置
-XX:PermSize 非堆内存的初始值,默认物理内存的1/64 ,也是最小非堆内存。
-XX:MaxPermSize 非堆内存最大值,默认物理内存的1/4。

例如:

-Xms512m 设置JVM促使内存为512m。此值可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存。

-Xmx512m ,设置JVM最大可用内存为512M。

-Xmn200m设置年轻代大小为200M。整个堆大小=年轻代大小 + 年老代大小 + 持久代大小。持久代一般固定大小为64m,所以增大年轻代后,将会减小年老代大小。此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8。

-Xss128k:设置每个线程的堆栈大小。JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K。更具应用的线程所需内存大小进行调整。在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右。

注:以上参数内容详情主要出自于网络。

参考:http://www.cnblogs.com/jack204/archive/2012/07/02/2572932.html
http://blog.csdn.net/tiercel2008/article/details/6956816
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息