JVM----内存区域及溢出异常

来源:互联网 发布:arm linux gcc -v 编辑:程序博客网 时间:2024/05/17 05:50

JVM内存区域

  

程序计数器:

一个较小的内存空间,可以看做是当前执行代码的行号指示器,分支、循环、跳转、异常处理、线程方面都要依赖这个计数器完成。

这个程序计数器是线程隔离的,即每个线程有唯一的程序计数器。Native方法是不会在程序计数器内计数的,只会为Null。

这个区域的异常不会导致OutOfMemoryError。

 

JVM栈:

主要用于描述java方法执行的内存模型,程序计数器只是单纯的记录行号,而JVM栈还保存了方法中的局部变量表、操作栈、动态链接、方法出入口等信息,对于很多人来说JVM栈的主要作用是存储了方法内的局部变量表(变量表直接存储基本数据类型和引用数据类型的引用),局部变量表所需内存在编译器就已经确定,不会改变。

这块内存区域和程序计数器一样都是线程隔离的,即各个线程只有一个。

这块内存区域当JVM栈请求的栈深度超出系统分配时,会报StackOverFlowError。

当这块内存区域在动态扩展时(一般都可以动态扩展),如果物理内存无法满足扩展请求,会报OutOfMemoryError。

 

本地方法栈:

和JVM栈相似,唯一的区别就是JVM栈是专门为JVM字节码方法准备的,而本地方法栈是为Native方法服务的

 

堆:

这是JVM中内存占用最大的地方,一般情况下对象实例以及数据都是在堆这里分配内存进行实例化。这里也是GC主要工作的地方。这里主要分为新生代、老生代及永生代区,后边说到的GC会对这里的分代进行说明。

这块内存区域是所有线程共享的。

当程序中的对象实例创建过多致使内存不够分配的时候,会造成OutOfMemoryError。

 

方法区:

这个区域主要存储已被虚拟机加载的类信息、常量、静态变量以及动态代理生成的类信息等。

和堆一样这块内存区域也是线程共享的。

同样,当无法完成内存分配时,抛出OutOfMemoryError。

 

栈中的reference类型定位到堆中对象实例的两种方式:

使用句柄方式:


使用直接指针方式(HotSpot使用):

 

溢出异常:

堆溢出

堆溢出就是给堆分配内存(不断创建引用类型对象),由于内存限制无法继续分配的情况,报错信息是java.lang.OutOfMemoryError: Java heapspace,在启动快照的情况下还有Dumping heap to java_pid2712.hprof ...这个信息指明快照文件。通过启用参数-XX+HeapDumpOnOutOfMemoryError,在Tomcat下可以通过修改catalina.shcatalina.bat文件修改JVM参数添加-XX:+HeapDumpOnOutOfMemoryError参数保存溢出快照

 

栈溢出

栈溢出一般情况下是指JVM栈、本地方法栈溢出,这种情况下的溢出一般分为两种情况:

单线程运行,一般报错信息为java.lang.StackOverflowError,这种情况有堆栈信息,比较容易排查

多线程运行,一般报错信息为java.lang.OutOfMemoryError

在我们实际开发中,单线程情况很少,因为我们使用的Web容器(Tomcat等)都是多线程的,所以较为常见的还是第二种情况。在第二种情况下,我们可以通过设置减少堆内存或减少栈容量来调优。主要原因是,栈的容量由 JVM内存-堆内存-方法区内存-程序计数器内存 来决定。

 

溢出快照分析

可以通过Eclipse的MAT(Memory Analyser Tools)来分析溢出产生的快照文件。该工具可以生成一个HTML格式的分析报告,通过异常发生时的内存占用饼状图、变量(对象)引用树的方式较为准确的报告出产生内存溢出的代码位置或相关信息。

0 0
原创粉丝点击