写点什么

JVM 内存划分

用户头像
Geek_571bdf
关注
发布于: 1 小时前

1. 谈谈 JVM 内存区域的划分,哪些区域可能发生 OutOfMemoryError?

上图反应了实际中 Java 进程内存的占用。可以看到,与 JVM 运行时数据区还是有差别的。区别在于多了“直接内存区域”和 Code Cache 等。

JVM 本身是个本地程序,还需要其他的内存去完成各种基本任务,比如,JIT Compiler 在运行时对热点方法进行编译,就会将编译后的方法储存在 Code Cache 里面;GC 等功能需要运行在本地线程之中,类似部分都需要占用内存空间。这些是实现 JVM JIT 等功能的需要,但规范中并不涉及。

 

// jvm 规范

线程共享:堆、方法区。线程独享(即,每个线程有一份):栈、程序计数器

  • 程序计数器:每个线程都拥有它自己的程序计数器,程序计数器存储的是正在执行的 Java 方法的 JVM 指令地址。

  • Java 虚拟机栈:方法调用伴随着栈帧的入栈和出栈,栈帧中存储着局部变量表、操作数(operand)栈、动态链接、方法正常退出或者异常退出的定义等。

  • 堆:线程共享,用来放置 Java 对象实例。

  • 方法区:线程共享,用于存储元数据,例如类结构信息,以及对应的运行时常量池、字段、方法代码等。

  • 运行时常量池:运行时常量池是方法区的一部分。编译期生成的各种字面量、符号引用等,都存储在常量池中。

  • 本地方法栈: Hotspot 中,本地方法栈和 Java 虚拟机栈是在同一块儿区域

 

2. 永久代问题。

// 过去,方法区有 perm 区实现。

JDK 7 以前,Intern 字符串的缓存和静态变量曾经都被分配在永久代上。而 JDK 8 将永久代移除,永久代被元数据区取代。需要注意的是,Intern 字符串缓存和静态变量并不是被转移到元数据区,而是直接在堆上分配。

因此,可以明确所有的对象实例都是创建在堆上。

 

2. 所谓 OOM,即,没有空闲内存,并且垃圾收集器也无法提供更多内存。

2. 除了程序计数器,其他区域都有可能会因为空间不足发生 OutOfMemoryError,简单总结如下:

  • 堆:“java.lang.OutOfMemoryError:Java heap space”。原因可能是:存在内存泄漏问题、堆的大小不合理等

  • 栈:当递归不断深入,JVM 会先抛出 StackOverFlowError,当 JVM 试图去扩展栈空间失败时,会抛出 OutOfMemoryError

  • 方法区:“java.lang.OutOfMemoryError: Metaspace”

用户头像

Geek_571bdf

关注

还未添加个人签名 2019.06.13 加入

还未添加个人简介

评论

发布
暂无评论
JVM内存划分