java内存图是干什么的_JVM有哪些内存区域,分别用来做什么的?我们就来把JVM开膛破肚...

JVM加载回顾

上次我解释了JVM是如何加载class的,以及有哪些加载器。请看下图我们在进行一次简单的回忆!

一个类从加载到使用,一般会经历下面的这个过程 看图:

加载->验证->准备->初始化->使用->卸载销毁

f801dd6f789e6826b4107b0da5f8406c.png

我们写的字节码如何运行的

7eb955f678fe520ffe6e1a2cfc93d8e3.png

我们写的代码被编译成字节码,实际上还是java虚拟机调用执行引擎来执行的。这里我就不详细说了,可以看我以前写的文章。

JVM到底是怎么划分内存区域的

1,首先我们写的java代码是不是要被加载到JVM进行运行,我给大家画了一个整体的架构图。

d6134a9c8d17dc24e3ac1782d7cde7fe.png 通过图解我们知道java代码被编译成了class,然后由我们的加载器加载到jvm,然后由java执行器执行编译好的字节码,在程序运行的时期就会用到数据和相关的信息,这个时候就需要java运行时区域,这个区域也是我们通常说的jvm内存,内存的管理也是对这个区域进行管理,比如分配内存和回收内存。 java运行时区域图解:

6ef4dac78be4ebc725fd06d5e51e9a69.png 这几个区域我不想像别人那样写一大堆文字,然后你们看了一点都不知道是什么东西。这个很难理解,接下来我将结合一些代码来讲解。

我们思考一个问题,我们假设我们的代码要存储哪些东西在运行时区域里面呢?我大致归纳了一下,看是否和你们想的一样哦。

c719bb621aac295003e7e528237f8674.png 首先我们写的所有的class是不是都要有一个地方存储啊?我们代码在运行的时候是不是要执行我们一个个的方法?在运行某个方法的时候,如果要创建对象是不是要有一个存放对象的地方啊?你有这些疑问的时候设计的人同样是这样的!

这个就是JVM为什么要划分不同的区域来存放了,它是为了我们写好代码在运行的过程中根据需要来使用。

存放我们类的方法区

JDK1.8以前叫做方法区,我现在用的是1.8的JDK,JDK8把这块区域叫做“元数据空间”。这个就专门存放我们写的各种类的信息。 结合我们代码来看看

package cn.changhong.conf.client.utils;

/**

* @author 独秀天狼

*/

public class Luancher {

public static void main(String[] args) {

Message message=new Message();

SendMsg sender=new DingdingSendMsg();

sender.send(message);

}

}

根据我原来说的只要用到了class就会加载到JVM看图理解比较好!

28e23e645ec7de8e4f9c500979bb23df.png

存放执行指令的程序计数器

/**

* @author 独秀天狼

*/

public class Luancher {

public static void main(String[] args) {

Message message=new Message();

SendMsg sender=new DingdingSendMsg();

sender.send(message);

}

}

当前我们这个代码会被编译成字节码,字节码就是计算器可以理解的语言,我们字节码的执行指令大概是如下,给大家看一下: 想看到代码的一些指令,可以用javap 来看我们的class转换到指令是什么样的。

public class cn.changhong.conf.client.utils.Luancher {

public cn.changhong.conf.client.utils.Luancher();

Code:

0: aload_0

1: invokespecial #1 // Method java/lang/Object."":()V

4: return

public static void main(java.lang.String[]);

Code:

0: new #2 // class cn/changhong/conf/client/utils/Message

3: dup

4: invokespecial #3 // Method cn/changhong/conf/client/utils/Message."":()V

7: astore_1

8: new #4 // class cn/changhong/conf/client/utils/DingdingSendMsg

11: dup

12: invokespecial #5 // Method cn/changhong/conf/client/utils/DingdingSendMsg."":()V

15: astore_2

16: aload_2

17: aload_1

18: invokeinterface #6, 2 // InterfaceMethod cn/changhong/conf/client/utils/SendMsg.send:(Lcn/changhong/conf/client/utils/Message;)V

23: return

}

E:\apollo_conf\target\classes\cn\changhong\conf\client\utils>

比如“0: aload_0”就是字节码指令,计算机读到这条指令就知道要做什么了。 所以在这里大家要明白:我们写的代码最终会被转换成字节码指令。

59522cdbdf7e54390c880b6595d63584.png 在字节码执行的时候就需要一个内存区域来记录我们执行指令的位置。如图

203029041bfe3112b0c081d3fdeddbda.png 我们的程序基本上都是多线程执行指令的,每个线程执行指令都会有一个独立的计数器,专门记录当前的线程执行指令到那个位置了。如图:

8918dab4ca7f19ed3a6703eaf7b1dbab.png 通过这样的图解是不是比较清晰了。

Java虚拟机栈

当java代码在执行的时候,一定是线程执行某个方法,比如下面我写出的这些代码,这个会有一个main线程去执行main方法的代码,当线程main执行main方法的时候,就会有对于的程序计数器来记录当前main线程执行的指令位置。

/**

* @author 独秀天狼

*/

public class Luancher {

public static void main(String[] args) {

Message message=new Message();

SendMsg sender=new DingdingSendMsg();

sender.send(message);

}

}

方法里面我们会有一些局部的成员变量,就当前这个代码就有message,sender因此我们JVM就必须要有一个区域来保存当前的局部变量等数据。这个就是java虚拟机栈。 每个线程都有自己的虚拟机栈,这个代码里面就会有main线程的虚拟机栈,用来存放自己执行的局部变量。当执行方法就会创建一个栈帧 这个栈帧就存储了局部变量,操作数栈,方法出口等,这个时候就会把main方法压入到main线程创建的虚拟机栈中,同时 message ,sender就会依次存放。如图所示!

df563839c3255d8d5f943b82c2810892.png 紧接着 sender.send()方法就来到了 DingdingSendMsg 里面的这个send方法,这个方法里面也有局部变量

public class DingdingSendMsg implements SendMsg {

@Override

public void send(Message message) {

String title=message.getTitle();

System.out.println(title);

//TODO 发送钉钉消息

}

}

那这个时候也会为send方法创建一个栈帧,压入到线程的虚拟机栈里面。如图:

822f3d0e34eb0df2e4ff586369b032cd.png

上述就是JVM中的java虚拟机栈整个图解过程,调用任何方法都会为这个方法创建一个栈帧然后入栈,这个栈帧里面就保存了局部变量的数据,和方法执行的相关的信息,执行完了就出栈。 最后我们把整个过程图解一下:

ca68d393470c9d6304245dfd38d136eb.png 以上就java虚拟机栈的整个图解,希望大家已经明白了。

java对象的存储 堆

我们现在已经知道了main线程执行main()方法,会有自己的程序计数器,还会把main方法的局部变量依次压入java虚拟机栈中存放。

在上面的图中还有一个非常关键的区域没有说明,那就是java堆内存,这个就是存放我们代码创建的各种对象的。 比如刚刚那些代码里面:

/**

* @author 独秀天狼

*/

public class Luancher {

public static void main(String[] args) {

Message message=new Message();

SendMsg sender=new DingdingSendMsg();

sender.send(message);

}

}

这个代码里面就有 new Message()和 new DingdingSendMsg()这2个对象创建,所以我们创建的这部分数据就会存储在堆中。

然后局部变量 message,sender引用了对象 Mesage,DingdingSendMsg,我用一个图来说明。

f2f0ddda18d19b45f51ad3a21f5893fb.png

总结

最后我将画一个完整的图给大家体会一下,这样就更加的清晰和明了。

8e19b2e941a3647511eb3e29ffed2dc6.png

谢谢大家的阅读,希望能给你们梳理清楚了这个难懂的JVM内存结构。面试再也不怕了,也真正的理解了,java工作的原理了。

喜欢就给我点赞。

本文中版权归独秀天狼团队所有,转载请标注清楚。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: JVM内存模型是Java虚拟机的内存管理模型,它用于管理Java应用程序的内存使用。JVM内存模型包括以下几个区: 1. 程序计数器:保存了当前线程执行的字节码的行号指示器,它是一个较小的内存区域,不会对Java对象造成影响。 2. 虚拟机栈:保存了Java方法执行的内部状态,每个方法执行的时候都会创建一个栈帧,用于存储局部变量表、操作数栈、动态链接、方法出口等信息。 3. 本地方法栈:与虚拟机栈类似,但专门用于执行本地(Native)方法。 4. 堆:是Java虚拟机所管理的最大一块内存区域,用于存放对象实例。所有对象实例以及数组都在堆上分配内存。 5. 方法区:存储了类信息、常量、静态变量、即时编译器编译后的代码等数据。 JVM通过这些内存区域管理Java应用程序的内存,保证程序的正常运行。 ### 回答2: JVMJava虚拟机)内存模型是用来管理Java程序运行时所需的内存的。JVM内存模型主要由以下五个区域组成,分别是:堆、方法区(也称为永久代)、虚拟机栈、本地方法栈和程序计数器。 1. 堆(Heap):堆是JVM内存中最大的一块区域,被用来存储对象实例和数组。堆被所有线程共享,而且在JVM启动时就创建了。堆被进一步划分为新生代和老年代。新生代包括Eden区和两个Survivor区(From和To区),它们主要用于存储新创建的对象。老年代用于存储长期存活的对象。 2. 方法区(Method Area):方法区用来存储类的元数据信息,包括类的结构、常量、静态变量、即时编译器编译后的代码等。另外,方法区还包含一个常量池,用于存储类的常量。 3. 虚拟机栈(VM Stack):虚拟机栈用于存储每个线程的方法调用和局部变量信息。每个线程在执行方法时都会在虚拟机栈中创建一个栈帧,栈帧包含了方法局部变量表、操作数栈、动态链接、方法出口等信息。 4. 本地方法栈(Native Method Stack):本地方法栈类似于虚拟机栈,但是它是为执行本地(即非Java)方法而设计的。它的功能和虚拟机栈类似。 5. 程序计数器(Program Counter):程序计数器是每个线程私有的,用于存储当前正在执行的指令地址。在任意时刻,一个线程都只能执行一个方法,程序计数器记录了当前线程执行的字节码指令位置,当线程被中断或者被调度时,可以恢复到正确的执行位置。 这些内存区域JVM内存模型中各自承担着不同的职责,通过合理的调配和管理,可以提高Java程序的性能和安全性。 ### 回答3: JVMJava虚拟机)内存由多个不同的区域组成,用于存储不同类型的数据。主要的JVM内存区域包括: 1. 程序计数器(Program Counter):用于记录当前线程执行的字节码指令的地址。在多线程环境下,每个线程都有自己的程序计数器。 2. Java栈(Java Stack):用于存储局部变量、方法参数、操作数栈和方法调用的信息。每个方法在执行时都会创建一个栈帧,栈帧包含了方法的局部变量和操作数栈信息。 3. 本地方法栈(Native Method Stack):用于存储执行本地方法(非Java代码实现的方法)的信息。 4. 堆(Heap):用于存储Java对象实例和数组。堆是JVM内存中最大的一块区域,可以被所有线程共享。在堆中,对象实例的分配和回收由垃圾回收器负责。 5. 方法区(Method Area):用于存储JVM加载的类信息、常量、静态变量、方法字节码等。方法区也被称为永久代(Permanent Generation),尽管在Java 8中,永久代已经被元空间(Metaspace)取代。 6. 运行时常量池(Runtime Constant Pool):是方法区的一部分,用于存储编译时生成的字面量和符号引用。常量池中的内容包括类和接口的全限定名、字段和方法的名称和描述符、字符串常量等。运行时常量池是每个类或接口的常量池表的运行时表示形式。 除了以上主要的内存区域外,还有一些其他的内存区域,比如直接内存(Direct Memory),用于存储通过NIO类库分配的字节缓冲区。直接内存并不是JVM中的一部分,但是它可以被JVM管理。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值