【JVM虚拟机面试宝典】JVM的内存结构是怎么样的?在JVM中会发生内存溢出的区域有那些?— day06
目录
说说JVM 内存区域?
JVM的内存结构是怎么样的?
在JVM中会发生内存溢出的区域有那些?
方法区、永久代、元空间有什么区别?
说说JVM 内存区域?
运行时内存划分如下区域:
方法区:存放类,静态变量,静态方法,常量(常量不等同于常量值,基本类型都属于常量值)
(局部常量,成员常量,静态常量),成员方法,线程不安全
堆区:存放分配的对象,线程不安全
栈区:存放局部变量,以及运行的方法.
程序计数器:记录当前线程走到哪一步了.
本地方法栈:运行本地方法,一般是其他语言(本地方法相当于库函数,封装了对操作系统的
JVM的内存结构是怎么样的?
说明
加粗字体代表了 JVM 虚拟机组件,对于 Oracle 的 Hotspot 虚拟机实现,不区分虚拟机栈和本地方法栈
以一个类为例,该类最底层的就是源代码,java Source是以.java结尾的源码文件。
通过javac命令编译源代码为字节码,以.class结尾的文件,
然后执行java命令运行,
创建 JVM,调用类加载子系统加载 class,将类的相关信息存入方法区中
创建 main 线程,是主线程被称为入口线程,使用的内存区域是JVM 虚拟机栈,开始执行 main 方法代码
如果遇到了未见过的类,会继续触发类加载过程,同样会存入方法区
需要创建的对象,会使用堆内存来存储对象
不再使用的对象,会由垃圾回收器在内存不足时回收其内存
调用方法时,方法内的局部变量、方法参数所使用的是 JVM 虚拟机栈中的栈帧内存
调用方法时,先要到方法区获得到该方法的字节码指令,由解释器将字节码指令解释为机器码执行
调用方法时,会将要执行的指令行号读到程序计数器,这样当发生了线程切换后,恢复时就可以从中断的位置继续运行
对于非 java 实现的方法调用,例如hashCoed(),使用内存称为本地方法栈(见说明)
对于热点方法调用,或者频繁的循环代码,由 JIT 即时编译器将这些代码编译成机器码缓存,在需要使用的时候直接从缓存中拿就可以了,提高了执行性能。
在JVM中会发生内存溢出的区域有那些?
除了不会出现的内存溢出区域,程序计数器以外,其他都有可能出现。
出现OutOfMemoryError(内存不足错误)的情况:
- 堆内存耗尽—》对象越来越多,又一直在使用,不能被垃圾回收。
- 方法区内存耗尽—》加载的类越来越多,或者很多框架都会在运行期间动态产生新的类。
- 虚拟机栈累积—》每个线程最多会占用1M内存,线程个数越来越多,而又长时间运行不销毁时。
出现StackOverflowErro(堆叠溢出错误)的情况:
- JVM虚拟机栈,原因有方法递归调用未正确结束,反序列化json时循环引用。
方法区、永久代、元空间有什么区别?
方法区:是 JVM 规范中定义的一块内存区域,用来存储类元数据、方法字节码、即时编译器需要的信息等
永久代:是 Hotspot 虚拟机对 JVM 规范的实现(1.8 之前)
元空间:是 Hotspot 虚拟机对 JVM 规范的另一种实现(1.8 以后),使用本地内存作为这些信息的存储空间
在堆内存中:当一个类加载器对象,这个类加载器对象加载的所有类对象,这些类对象对应的所有实例对象都没人引用时,GC 时就会对它们占用的对内存进行释放。
元空间中:内存释放以类加载器为单位,当堆中类加载器内存释放时,对应的元空间中的类元信息也会释放。