概述
JVM这个话题是面试的时候经常问到一个难点,今天将会从内存管理的角度来介绍一下jvm,垃圾收集机制为我们打理了很多繁琐的事情,但是也仍然不是万能的,懂得JVM的内部内存结构,工作机制,是设计高扩展性应用和诊断运行时问题的基础。
今天的问题是,从内存角度看,哪些区域会产生OutOfMemoryError
概述
通常会将JVM内存划分为几个方面:
第一,程序计数器(PC)。在JVM中,每个线程都有自己的PC,并且任何线程都只执行一个方法。PC会去存储当前线程执行的Java方法的JVM指令地址。或者如果是在执行本地方法,则是undefined。
第二,Java虚拟机栈。每个线程在创建的时候都会创建一个虚拟机栈,其内部保存一个个的栈帧,对应着一次次的Java方法调用。
且在同一时间内,不光是一个线程只对应一个方法,同样的,一个线程里一次也只运行一个栈帧,当此栈帧对应的java方法执行结束后,新的栈帧会被创建出来进行压栈操作,而旧的栈帧会出栈。而压栈和出栈是由JVM控制的。
栈帧中有局部变量表、操作数栈、动态链接、方法正常退出或者异常退出的定义。
第三,堆。它是管理Java内存的核心,存放Java对象,保存对象的实例。同时堆具有可见性,被所有线程共享,在JVM启动之后,用“Xmx”之类参数表示最大堆空间指标
同时,堆也是垃圾收集器活跃的区域,堆内的空间会被不同的垃圾收集器进行进一步的细分。
第四,方法区。方法区同样具有可见性,被线程所共享,存储元数据信息(都知道namenode),像类结构信息对应的常量池、字段、方法代码等等。在JDK 8出现之后,方法区被改名为元数据区。
第五,运行时常量池,方法区的一部分,存放常量信息。
第六,本地方法栈,一个线程对应一个,支持对本地方法进行调用。
扩展
对以上六中JVM内存结构进行一个图示:
这张图反应了实际Java中的进程内存占用,与规范中定义的JVM运行时候数据的差异,都可以看做是数据区的一个超集,只要是Java进程在运行的时候占用内存,都会影响到工程实践。
主要介绍几点区别:
- 直接内存区域,并未体现在JVM内存模型之中,由Derect Buffer直接分配内存,易出现问题。
- JVM本身是个本地程序,还需要各种其他内存完成其他基本任务,比如之前提到的java.compiler底层和javac对等的编译,就会将编译后的结果存在Cache内。
- GC等功能需要运行在本地线程中(后续专栏会介绍到GC调优相关),类似的都需要一定的内存占用。
如果深入到jvm的细节,就会发现一些结论似乎有些模棱两可,比如:
- Java对象是不是都创建在堆上?
通过逃逸分析可知,jvm会在栈上分配那些不会逃逸的对象。但是根据逃逸分析相关的文档中可知,一切一切的对象实例都是创建在堆上的。
逃逸分析
:逃逸分析是一种确定指针动态范围的方法——分析在程序的哪些地方可以访问到指针。 它涉及到指针分析和形状分析。 当一个变量(或对象)在子程序中被分配时,一个指向变量的指针可能逃逸到其它执行线程中,或是返回到调用者子程序。
- 那么,什么是OOM(OutOfMemoryError)问题呢,会在哪些内存区域发生呢?
当JVM内存不够使用就会报错,此时就连垃圾收集器也无法提供更多的内存。
在抛出OOM之前,垃圾收集器就会被触发,及其所能清理出的空间。里面涉及到了一个gc的调优方法,在后续中会讲到。
但是需要注意的是,并不是垃圾收集器在任何时候都会被启动,如果我们分配的对象超过了堆的最大值,JVM可以判断出垃圾收集器无法解决这个问题,就会抛出oom。
从前面的分析,得出以下结论:
- 除了PC,其他JVM区域都会有可能发生错误导致OOM
- 堆导致OOM:抛出异常java.lang.OutOfMemoryError:Java heap space,可能原因是内存泄露,可能是内存大小不合理
- Java虚拟机栈和本地方法栈导致OOM:不断递归且不退出调价导致压栈,就会抛出Stack OverflowError
- 直接内存不足导致OOM
当产生OOM时候,调取GC日志查看问题也是解决问题的方法之一。
最后
以上就是健康戒指为你收集整理的谈谈JVM内存区域的划分,哪些区域可能发生OutOfMemoryError?的全部内容,希望文章能够帮你解决谈谈JVM内存区域的划分,哪些区域可能发生OutOfMemoryError?所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复