JVM内存区域与内存溢出异常
Java虚拟机在执行java程序时会把它所管理的内存会分为若干个不同的数据区域,不同的区域在内存不足时会抛出不同的异常。
一、运行时数据区域的划分
(1)程序计数器
程序计数器(Program Counter Register)是一块比较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器;
PCR为线程私有内存,程序计数器是唯一一个在Java虚拟机规范中没有规定任何OOM情况的区域。
(2)方法区
方法区(Method Area)与Java堆一样,是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
也称为永久代(Permanent Generation)但随着Java8的到来,已放弃永久代改为采用Native Memory来实现方法区的规划。
此区域回收目标主要是针对常量池的回收和对类型的卸载。
(3)JVM栈
虚拟机栈(Java Virtual Machine Stacks)描述的是Java方法执行的内存模型:每个方法在在执行的同时都会创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法接口等信息。每个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈出栈的过程。
Java虚拟机栈也是线程私有,它的生命周期与线程相同。
Java内存区常分为堆内存(Heap)和栈内存(Stack);
OOM情况:
线程请求的栈深度>虚拟机所运行的最大深度;
虚拟机动态扩展时无法申请到足够的内存
(4)本地方法栈
与虚拟机栈作用很相似,区别是虚拟机栈为虚拟机执行java方法服务,而本地方法栈则是为虚拟机用到的Native方法服务。和虚拟机栈一样可能抛出StackOverflowError和OutOfMemoryError异常。
(5)Java堆
Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建。主要存放对象实例。
Java堆是垃圾收集器管理的主要区域,其可细分为新生代和老年代。如果在堆中没有内存完成实例分配,并且也无法再扩展时,会抛出OutOfMemoryError异常。
(6)直接内存
直接内存(Direct Memory)并不是虚拟机运行时数据区的一部分,也不是虚拟机规范中定义的内存区域。
能在一些场景中显著提高性能,因为避免了在Java堆和Native堆中来回复制数据。
直接内存的分配不会受到Java堆大小的限制,但会收到本机总内存(RAM以及SWAP/分页文件)大小以及处理器寻址空间的限制。
设置Xmx等参数信息时注意不能忽略直接内存,不然会引起OOM。
二、对象的访问定位
Java程序需要通过栈上的reference数据来操作堆上的具体对象,
对象访问方法取决于不同的JVM实现,目前主流访问方式有使用句柄和直接指针2种。
(1)句柄访问
Java堆中划分出一块内存作为句柄池,reference中存储对象的句柄地址,句柄中包含对象实例数据与类型数据各自的具体地址信息;
(2)直接指针访问
Java堆对象的布局中必须考虑如何放置访问类型数据的相关信息,reference中存储对象地址;
(3)两种访问方式比较
使用句柄访问最大的好处是reference中存储的是稳定的句柄地址,在对象被移动(GC时移动对象是很普遍的行为)时只会改变句柄中的实例数据指针,而reference本身不需要修改;
使用直接指针访问方式的最大好处是速度更快,它节省了一次指针定位的时间开销,由于对象访问在Java中非常频繁,因此这类开销积少成多后也是一项非常可观的执行成本;
HotSpot虚拟机采用指针访问方式进行对象访问,从整个软件开发范围看,各种语言和框架使用句柄来访问的情况也非常常见。
三、几种内存溢出异常及解决
虚拟机主要的几种异常是OutOfMemoryError异常和虚拟机栈和本地方法栈溢出,以及运行时常量池溢出等。
(1)OutOfMemoryError异常
除了程序计数器外,虚拟机内存的其他几个运行时区域都有发生OutOfMemoryError(OOM)异常的可能,
java堆用于存储对象实例,我们只要不断的创建对象,并且保证GC Roots到对象之间有可达路径来避免垃圾回收机制清除这些对象,就会在对象数量达到最大堆容量限制后产生内存溢出异常。
出现这种异常,一般手段是先通过内存映像分析工具(如Eclipse Memory Analyzer)对dump出来的堆转存快照进行分析,重点是确认内存中的对象是否是必要的,先分清是因为内存泄漏(Memory Leak)还是内存溢出(Memory Overflow)。
如果是内存泄漏,可进一步通过工具查看泄漏对象到GC Roots的引用链。于是就能找到泄漏对象时通过怎样的路径与GC Roots相关联并导致垃圾收集器无法自动回收。
如果不存在泄漏,那就应该检查虚拟机的参数(-Xmx与-Xms)的设置是否适当。
(2)虚拟机栈和本地方法栈溢出
如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverflowError异常。
如果虚拟机在扩展栈时无法申请到足够的内存空间,则抛出OutOfMemoryError异常
这里需要注意每个线程分配到的栈容量越大,可以建立的线程数就越少,建立线程时候就越容易耗尽剩余内存。
按虚拟机默认参数,栈深度在大多数情况下达到1000~2000完全没问题,对于正常方法调用(包括递归),这个深度应该完全够用;
但如果是建立过多线程导致内存溢出,在不能减少线程数或者更换X64位虚拟机的情况下,就只能通过减少最大堆和减少栈容量来换取更多的线程
(3)方法区和运行时常量区溢出
java.lang.OutOfMemoryError: PermGen space
运行时常量池溢出(HotSpot虚拟机中的永久代),
如果要向运行时常量池中添加内容,最简单的做法就是使用String.intern()这个Native方法。
运行时常量池溢出(HotSpot虚拟机中的永久代),
如果要向运行时常量池中添加内容,最简单的做法就是使用String.intern()这个Native方法。
该方法的作用是如果池中已经包含一个等于此String的字符串,则返回代表池中这个字符串的String对象;否则,将此String对象包含的字符串添加到常量池中,并且返回此String对象的引用。
由于常量池分配在方法区内,我们可以通过-XX:PermSize和-XX:MaxPermSize限制方法区的大小,从而间接限制其中常量池的容量。
四、Java如何获得JVM内存使用情况
要获得JVM相关的内存信息,需要使用Runtime类的totalMemory(), maxMemory() 和 freeMemory()三个方法。
- import java.text.DecimalFormat;
- public class Test{
- /**
- * 显示JVM总内存,JVM最大内存和总空闲内存
- */
- public void displayAvailableMemory() {
- DecimalFormat df = new DecimalFormat(“0.00″) ;
- //显示JVM总内存
- long totalMem = Runtime.getRuntime().totalMemory();
- System.out.println(df.format(totalMem 1000000F) + ” MB”);
- //显示JVM尝试使用的最大内存
- long maxMem = Runtime.getRuntime().maxMemory();
- System.out.println(df.format(maxMem 1000000F) + ” MB”);
- //空闲内存
- long freeMem = Runtime.getRuntime().freeMemory();
- System.out.println(df.format(freeMem 1000000F) + ” MB”);
- }
- /**
- * Starts the program
- * @param args the command line arguments
- */
- public static void main(String[] args) {
- new Main().displayAvailableMemory();
- }
- }
参考
JVM内存区域与内存溢出异常的更多相关文章
- JVM基础知识(1)-JVM内存区域与内存溢出
JVM基础知识(1)-JVM内存区域与内存溢出 0. 目录 什么是JVM 运行时数据区域 HotSpot虚拟机对象探秘 OutOfMemoryError异常 1. 什么是JVM 1.1. 什么是JVM ...
- 深入理解jvm之内存区域与内存溢出
文章目录 1. Java内存区域与内存溢出异常 1.1. 运行时数据区域 1.1.1. 程序计数器 1.1.2. java虚拟机栈 1.1.3. 本地方法栈 1.1.4. Java堆(Java Hea ...
- 深入理解java虚拟机系列(一):java内存区域与内存溢出异常
文章主要是阅读<深入理解java虚拟机:JVM高级特性与最佳实践>第二章:Java内存区域与内存溢出异常 的一些笔记以及概括. 好了開始.假设有什么错误或者遗漏,欢迎指出. 一.概述 先上 ...
- 深入理解java虚拟机---->java内存区域与内存溢出异常
2. java内存区域于内存溢出异常 2.1 概述: 对于C/C++而言,内存管理具有最高的权利,既拥有每一个对象的“所有权”,又担负着每一个对象生命开始到结束的维护责任. 对于java而言,则把内存 ...
- 第二章Java内存区域与内存溢出异常
第二章 Java内存区域与内存溢出异常 一.概述 对与Java程序员来说,在虚拟机自动内存管理机制的帮助下,不再需要为每个new操作去写delete/free代码,不容易出现内存泄露和内存溢出问 题, ...
- 《深入理解java虚拟机》第二章 Java内存区域与内存溢出异常
第二章 Java内存区域与内存溢出异常 2.2 运行时数据区域
- 深入了解Java虚拟机(1)java内存区域与内存溢出异常
java内存区域与内存溢出异常 一.运行时数据区域 1.程序计数器:线程私有,用于存储当前所执行的指令位置 2.Java虚拟机栈:线程私有,描叙Java方法执行模型:执行方法时都会创建一个栈帧,存储局 ...
- 2.1 自动内存管理机制--Java内存区域与内存溢出异常
自动内存管理机制 第二章.Java内存区域与内存溢出异常 [虚拟机中内存如何划分,以及哪部分区域.什么样代码和操作会导致内存溢出.各区域内存溢出的原因] 一.运行时数据区域 Java虚拟机所管理的内存 ...
- JVM-Java内存区域与内存溢出异常
第二章:Java内存区域与内存溢出异常 2.1 运行时数据区 1:程序计数器 : 线程私有,较小的内存空间,当前线程所执行的字节码的行号指示器,唯一不会发生错误的内存区域 2:虚拟机栈: 线程私有, ...
随机推荐
- phpcms 根据条件调取内容
asdasdsd <script> 正则 截取 function getUrlParam(name) { var reg = new RegExp("(^|&)" ...
- qt-5.6.0 移植之纯净的linux文件系统的建立
为什么要建立一个最纯净的文件系统,一开始是想在qt-4.8.5的文件系统基础之上加东西,慎重想了一下,这方法行不通,以为有很多东西不熟悉.干脆就自己建立一个. 步骤很简单: 一:下载一个bulidro ...
- 3.创建基本的AngularJS应用
1.1.模块 AngularJS引入了代表应用程序组件的模块的概念.模块提供命名空间,以基于模型的名称来引用指令,范围和其他组件.使得包装和再利用应用程序的部件更容易. AngularJS中,每个视图 ...
- ajax初探01
1.为什么使用ajax ajax使用异步处理模型,意味着在浏览器等待数据加载期间,用户可以做其他事情 在页面正在加载时使用ajax:使用ajax,浏览器可以向服务器请求一些数据,并且一旦数据请求发出, ...
- jQuery常用API
jQuery API查询网址 http://jquery.cuishifeng.cn/ Dom和jquery相互装换 jquery对象[0] => Dom对象 Dom对象 => $(Dom ...
- Java计算程序运行时间
public static void main(String[] args) { // TODO Auto-generated method stub long nd = 1000 * 24 * 60 ...
- PyQt4软件打包成exe文件
使用py2exe进行打包 例: from distutils.core import setupimport py2exeimport sys sys.argv.append('py2exe') py ...
- 套接字Socket
TCP/IP(Transmission Control Protocol/Internet Protocol)即传输控制协议/网间协议,是一个工业标准的协议集,它是为广域网(WANs)设计的.UDP( ...
- 最近开始做Android了
最近开始做Android,在学习的过程中发现找以前知识很不方便啊,于是决定以后还是把知识记录在博客里吧,说不定也能为他人提供参考!
- jquery checkbox 限制多选的个数
2015年11月6日 16:32:49 选中第四个的时候提示超过了3个, 点解alert框取消后, 将最后一个选中的checkbox取消选中 <script> $(document).re ...