JVM 理解(二)

JVM 理解(一)谈到了jvm工作机制、内存模型、以及内存的垃圾回收算法

今天继续深入,再谈谈jvm内存区域划分 和 哪些区域可能发生OutOfMemory

JVM内存区域组成部件分析

程序计数器(Program  Counter Register)

      在JVM规范中,每个线程都有它自己的程序计数器,任何时间一个线程都有一个方法在执行,有就是所谓的当前方法。程序计数器会存储当前线程正在执行的Java方法的JVM指令地址,如果在执行本地方法,未未指定值。

 Java虚拟机棧(Java Virtual Meachine Stack)

每个线程创建都会创建一个虚拟机棧,内部保存一个一个棧帧(Stack Frame) ,对应一次次的方法调用。

前边谈到程序计数器谈到了当前方法,同理在一个时间点,对应的只会有一个活动的棧帧,通常称为当前帧。方法所在的类叫当前类。如果在该方法中调用其它方法,对应的新的棧帧会创建出来,称为新的当前帧,一直到它返回结果或者执行结束。JVM直接对java棧的操作只有两个,即对棧帧的压棧和出棧。

棧帧中存储着局部变量表、操作数棧、动态链接、方法正常或者异常退出的定义。

堆(heap)

java内存管理的核心区。放置java对象实例,几乎所有创建的java对象实例都被直接分配在堆中。堆被所有的线程共享,在虚拟机启动时,制动的‘Xmx’等参数就是为了指定堆空间的指标。

堆也是垃圾回收器重点光顾的区域,堆空间还会被不同的垃圾收集器进一步细分,如分代划分

方法区(Method Area)

和堆一样,也是所有线程棧共享的一块内存区域,用于存储元数据 , 如 类结构信息、对应的运行时常量池、字段、方法代码等

另外,早期的Hotspot JVM实现,很多人习惯将方法区称为永久代,jkd8中将永久代移除,增加了元数据区。

运行时常量池(Run-Time Constant Pool)

方法区中谈到过,是属于方法区的一部分,分析反编译类文件能发现 版本号、字段、方法、超类、接口等信息,还有一项信息即常量池。java的常量池可以存放各种常量信息,无论是编译期生成的各种字面常量,还是需要在运行时决定的符号引用,它比一般的符号表存储的信息更加宽泛。

本地方法棧(Native Method Stack)

和java虚拟机非常相似,至此对本地方法的调用,每个线程都会创建一个,在Oracle Hotspot jvm中本地方法棧和java虚拟机棧是在同一块区域。

内存结构图总结

上边谈到了几个组成部分的概念和作用。一个图集成他们。

反映了实际中java进程内存占用,与规范中定义的JVM运行时数据区之间的差别。可以看作运行时数据区的超集,理论上的视角和现实中的视角是有区别的规范侧重通用、无差别部分,而应用开发者而言,只要是java进程在运行时会占用,都会影响到工程时间。

直接内存它就是Direct Buffer 所直接分配的内存,也是最容易出现问题的地方。在jvm工程师眼中,并不人为它是jvm内存中的一部分,也并未体现jvm内存模型当中。

jvm本身是本地程序,还需要其它内存区完成各种基本人为,比如 JIT Compiler在运行时,对热点地方进行编译,会将编译后的方法存储在code cache中。GC等功能需要运行在本地线程之中,类似部分都需要占用内存空间。这些都是实现JVM JIT功能的需要,但是在规范中未涉及。

关于OOM

JVM内存不够用时,会造成OOM,也就是说没有空闲空间,并且垃圾收集器也无法提供更多内存。

在抛出OutOfMemoryError之前,垃圾收集器会触发,尽可能区清理出空间。但不是在任何情况下,垃圾收集器都会被触发,比如分配一个超大对象,类似超级数组超过堆的最大值,JVM可以判断垃圾收集并不能解决这个问题,也会直接抛出OOM。从数据区的角度,除了程序计数器,其它区域都可能因为空间不足而产生OOM。

内存不足是OOM最常见诱因之一,产生不足的原因很多:可能是堆的大小不合理(比如处理比较客观的数据量,但是没有显示的指定堆的大小或者指定的太小);可能是内存泄漏问题;可能处理引用不及时导致堆积,内存无法释放

对于java虚拟机棧和本地方法棧,如果不断的进行递归调用而没有退出条件,会导致不断的压棧。JVM会抛出StackOverFlowError

老版本jdk永久代大小有限,jvm堆永久代回收非常不积极,当我们不断的添加新类型的时候,永久代出现OOM也会非常多,尤其在运行时存在大量动态类型生成的场合,类似Intern字符串缓存会占用空间,也会导致OOM:PermGen space;

随着元数据区域引入,方法内存已经不那么尴尬,堆相应的OOM也有所改善。OOM:Metaspace;

直接内存不足,也会造成OOM。首先,它不在堆上,所以Xmx之类的参数并不影响Direct Buffer等堆外成员的内存额度,在计算java可使用内存时,不能只考虑堆的需要,还要考虑直接内存等对外内存。如果出现内存不足的异常,也有可能是对外内存导致。

大多数垃圾收集过程中,不会主动去收集Direct Buffer,它的垃圾回收过程是基于Cleaner 和 幻引用对象机制实现的,本身不是public类型,内部实现了一个Deallocator负责销毁的逻辑,对他的销毁往往要拖到full gc,所以使用不当往往会造成oom。

如何分析OOM

首先要了解jvm内存。可以使用jconsole、visualvm等图形化工具,直观的观察java进程,掌握内存的使用情况。如JConsole可以直观的查看常见的堆内存和各种堆外内存的使用情况。

也能使用命令行进行分析,如 jstst和jmap等可以查看堆、方法区等。使用jmap等命令生成堆转储文件(Heap Dump),利用jhat或者eclipse mat等转储分析工具进行详细分析。

另外gc日志等输出,同样能看到很多信息

这里暂且提及一些理论后续会堆OOM的解决进行深入讨论

https://www.cnblogs.com/lishun1005/p/6019678.html

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值