【后端面经-Java】JVM内存分区详解

端面,java,jvm,内存,分区,详解 · 浏览次数 : 46

小编点评

**JVM内存分区简介** **Java虚拟机内存(JVM)**是一个虚拟化内存系统,它与操作系统共享内存,但运行时与应用程序隔离。 **JVM内存主要由以下几个区域组成:** * **栈(Stack):**用于存储局部变量、形参、返回地址等信息。每个线程最多有1个栈帧,栈帧的大小可以动态设置或固定分配。 * **堆(Heap):**用于存放对象实例(对象)。对象实例是 Java 中封装对象的封装。 * **方法区(Method Area):**用于存放已被加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。方法区是线程共享的,每个方法调用都会产生一个栈帧,出入栈操作实现方法的嵌套调用。 * **常量池(Constant Pool):**包含静态常量和运行时常量。静态常量在编译时就能够确定的数据,运行时常量则在方法区或堆中创建。 **JVM内存分配实例** ```java public class Demo { String username; public void method() { int i = 1; System.out.println("执行类方法"); } public static void main(String[] args) { int i = 1; String str = "hello java"; Demo demo = new Demo(); demo.username = "123"; demo.method(); } } ``` **面试模拟Q:JVM堆、栈、方法区的作用** **堆:** * 线程共享的,可动态分配内存。 * 存放对象实例、数组等数据。 **栈:** * 存储方法调用时的局部变量、形参、返回地址等信息。 *每个方法调用都会产生一个栈帧,出入栈操作实现方法的嵌套调用。 **方法区:** * 存放一些静态变量和常量数据。 * 包含类编译的方法和属性信息。

正文

@

1. JVM内存分区简介

JVM内存分区如图所示:

主要有如下几个区域:

  • 栈(Stack)
  • 堆(Heap)
  • 方法区(Method Area)
  • 程序计数器(PC)
  • 本地方法栈(Native Method Stack)

其中,程序计数器用于存储线程当前执行的指令地址(记录进度),程序计数器是线程私有的;
本地方法栈并不是每个JVM都必须实现,而是针对支持native本地方法调用的JVM。

  • 本地方法:使用非Java语言定义的方法;
  • 本地方法栈同一般的JVM栈一样,可分配固定或者动态内存。

剩余的三个区域:栈、堆、方法区,在下文中会详细介绍。

2. JVM栈

Java栈的基本存储单元为栈帧,每个线程对应一个栈帧,不同栈帧之间不会共享数据。
栈帧主要包括:

  • 局部变量:在方法内部定义的变量
  • 操作数栈:函数形参
  • 指向运行时常量池的引用
  • 方法返回地址
  • 附加信息

JVM栈的内存大小可以固定设置也可以动态分配,当出现栈溢出的时候,固定内存的栈会抛出StackOverFlowError,而动态分配的栈则会抛出OutOfMemoryError

3. JVM堆

JVM堆中存放的是对象实例(说人话就是用new创建的对象,因为数组也是new int[]这种形式创建的,所以JVM将数组看作一种特殊的对象)。
JVM堆是线程共享的,可动态分配内存。
需要注意的是,当一个方法结束之后,JVM栈中的相关数据会出栈,释放内存,但是对于JVM堆,虽然创建对象的方法已经消失,但是对象本身并不会在堆中被销毁,而是会等待垃圾回收机制进行内存回收。

4. JVM方法区

JVM方法区主要用于存放已被加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
方法区是线程共享的。
方法区中有个区域叫做常量池,包括静态常量池运行时常量池
静态常量池存放的是编译时就能够确定的常量数据,包括一些常数、类信息、方法信息等等;
运行时常量池存放的是运行时才能够确定的常量数据。

需要注意的是,运行时常量池在JDK1.7之后不再放在方法区中,而是放在堆中。

因此,对于常见的字符串常量池,如果一个字符串在编译时可以确定值,那么就放在方法区静态常量池中,如果在运行时遇到字符串先查找常量池,常量池中没有该字符串,则该字符串被创建,创建的对象会放在运行时常量池中,放在方法区还是堆中就需要根据版本来确定。

5. JVM内存分配实例

public class Demo {
	String username;
	public void method() {
        int i=1;
		System.out.println("执行类方法");
	}
	public static void main(String[] args) {
		int i=1;
		String str="hello java";
		Demo demo=new Demo();
		demo.username="123";
		demo.method();
		
	}
 
}
//出处:https://blog.csdn.net/m0_57816620/article/details/127332057?spm=1001.2101.3001.6650.1&utm_medium=distribute.pc_relevant.none-task-blog-2~default~CTRLIST~Rate-1-127332057-blog-125451232.235%5Ev38%5Epc_relevant_anti_t3&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2~default~CTRLIST~Rate-1-127332057-blog-125451232.235%5Ev38%5Epc_relevant_anti_t3&utm_relevant_index=2

对于上面的Java代码,各位可以思考一下内存分配的具体情况。

  1. Demo类编译的时候产生.class文件,放在方法区的静态常量池中,存储类信息;
  2. 从Demo类的main方法进入,方法main的内存地址入栈,
  3. int i=1;创建一个局部变量i,值为1,入栈;
  4. String str="hello java";创建一个字符串变量str,入栈;对于hello java这个字符串常量,JVM查找静态常量池运行时常量池中是否存在该字符串,如果存在,字符串变量str直接指向该字符串常量,如果不存在,则创建一个字符串常量hello java,并将该字符串常量放入运行时常量池中,然后字符串变量str指向该字符串常量;
  5. Demo demo=new Demo()创建一个对象变量demo,入栈;对于对象实例本身,则会创建之后存于堆中,然后demo变量指向该对象实例;
  6. demo.username="123"修改类中的属性值,在堆中设置username变量,值为123;
  7. demo.method()调用类方法,方法定义存于JVM方法区中,取出该方法信息,然后进入method方法,开启新一个线程,JVM栈帧加1,存放method方法的内存地址;
  8. 执行method方法,int i直接将局部变量入栈;
  9. System.out.println("执行类方法");则是调用System.out.println方法(库方法);
  10. method方法执行完毕,栈帧出栈;
  11. main方法执行完毕,栈帧出栈。
  12. 栈中无栈帧,结束进程;而此时堆中依然保留Demo对象实例,等待垃圾回收机制回收。

面试模拟

Q:JVM堆、栈、方法区的作用
A:JVM栈存放方法调用时的局部变量、形参、返回地址等信息,每个方法调用的时候都会产生一个栈帧,出入栈操作实现方法的嵌套调用;
堆中存放的是对象实例或者数组,是线程共享的,可动态分配内存;
方法区存放的是一些静态变量和常量数据,包括类编译的方法和属性信息,静态常量池和运行时常量池在JDK1.6均属于此类。

参考资料

  1. Java JVM 中 堆,栈,方法区 详解
  2. Java堆、栈、方法区、常量池
  3. java内存分配(堆,栈,方法区,常量池)图解
  4. Java内存模型(JMM)总结

与【后端面经-Java】JVM内存分区详解相似的内容:

【后端面经-Java】JVM内存分区详解

本文主要介绍了JVM内存分区的基本情况,着重介绍了栈、堆、方法区的分区情况,并给出实际代码解释内存分配的过程。

【后端面经-Java】JVM垃圾回收机制

本文对JVM垃圾回收机制做了详细解释,从"where"、"which"、"when"、"why"、"how"、"who"的角度,重点介绍JVM垃圾回收机制的触发机制、垃圾判断算法、垃圾回收算法和垃圾回收机制。

【后端面经-Java】Java创建线程的方法简介

本文简要介绍了java中创建线程的四种方式,并介绍了线程概念,适合新手阅读。

【后端面经-java】java线程池满的处理策略

本文对java线程池做了具体介绍,并且讨论了java线程池满了之后的拒绝策略。

【后端面经-Java】HashMap详解

本文详细介绍了hashmap,包括基本概念、hashmap数据结构、关键变量和重要方法,并且结合源码进行分析。

【后端面经-Java】Synchronize和ReentrantLock区别

本文介绍了Synchronize和ReentrantLock同步锁的相似和不同点,并指出两者的主要特点和适用场景。

【后端面经-Java】AQS详解

本文介绍了AQS的核心思想、基本架构、实现方法,并对框架中的重要源码方法进行介绍和分析

【后端面经-Java】公平锁和加锁流程

本文主要介绍了公平锁和非公平锁的概念和区别,并且结合Reentranslock锁的源码对加锁机制进行分析。

【后端面经-Java】I/O多路复用 简录

本文主要介绍了Java当中常见的几种IO模型,介绍其运行机制和实际缺点,并进行技术对比,对于IO多路复用的实现方式进行分析。

【后端面经-Java】String与StringBuffer与StringBuilder的比较

本文从String、StringBuffer、StringBuilder三者的特性出发,详细介绍三者的不同之处。