当前位置:首页 > Java技术 > JVM-内存模型

JVM-内存模型

2022年09月17日 17:06:06Java技术6

  阅读相关资料,自己先画了一个jvm内存模型草图

JVM-内存模型 _ JavaClub全栈架构师技术笔记

 

 

 

 

 

  类装在子系统不过多解释,上一篇类加载机制说的就是这个。

  字节码执行引擎(执行编译好后class文件指令码的程序),为C语音实现,不可见,不展开讲,下面主要来看内存模型中的5块。

  

  芜湖起飞~

 

  先从栈开始讲:

    栈后面加了个括号,线程,栈就是线程在执行方法的时候存放的一些方法内部的局部变量。

    当一个线程执行方法,会在栈里面开辟这个线程的栈空间。栈中有n个线程方法空间,我们看下面的程序

 

public class Stack {


    public int add() {
        int a =1;
        int b = 2;
        int c = (a+b) * 2;
        return c;
    }



    public static void main(String[] args) {
        Stack stack = new Stack();

        stack.add();
    }

}

  我们执行main函数,开启主线程,这个时候,栈会开辟一个main线程空间,如下图:

JVM-内存模型 _ JavaClub全栈架构师技术笔记

 

 

 

  这里来解释一个重要的概念:先进后出。

    我们看程序,先执行main方法:这时候,main方法栈帧入栈(栈帧:每个线程开启时对应的方法内存区域)

JVM-内存模型 _ JavaClub全栈架构师技术笔记

 

 

 

 

 

   然后main方法调用add方法,这个时候add方法入栈:

JVM-内存模型 _ JavaClub全栈架构师技术笔记

 

 

  然后add方法执行完了,add方法出栈:

 

JVM-内存模型 _ JavaClub全栈架构师技术笔记

 

 

 

  最后是main方法执行完成,main方法出栈:

JVM-内存模型 _ JavaClub全栈架构师技术笔记

 

 

  上面这个过程很好的解释了栈的先进后出的概念。

 

 

  上图中每一个方法在对应线程的栈空间中都有与之相对应的栈帧,那么栈帧中又有啥呢?看下图:

JVM-内存模型 _ JavaClub全栈架构师技术笔记

 

   

   里面有一些东西,为了更方便弄懂这些,我们来做以下操作:

    1:打开cmd,进入到Stack类的字节码文件所在地;

    2:执行javap -c Stack.class > Stack.txt;

    3:打开Stack.txt文件;

    4:打开java指令码操作手册(没有的百度);

   这个时候我们可以来看Stack.txt里面都是啥:

Compiled from "Stack.java"
public class com.ghsy.user.test.Stack {
  public com.ghsy.user.test.Stack();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public int add();
    Code:
       0: iconst_1
       1: istore_1
       2: iconst_2
       3: istore_2
       4: iload_1
       5: iload_2
       6: iadd
       7: iconst_2
       8: imul
       9: istore_3
      10: iload_3
      11: ireturn

  public static void main(java.lang.String[]);
    Code:
       0: new           #2                  // class com/ghsy/user/test/Stack
       3: dup
       4: invokespecial #3                  // Method "<init>":()V
       7: astore_1
       8: aload_1
       9: invokevirtual #4                  // Method add:()I
      12: pop
      13: return
}

   我们来看add方法中有啥,看Code下面第一行:

    0:iconst_1 - 将int类型值1压入栈(为啥是下标1不是0呢?0对应的是当前类对象this),这是个怎样的过程呢?看下图:

 JVM-内存模型 _ JavaClub全栈架构师技术笔记

 

 

 

   再看第二行:

    1:istore_1 - 从栈中弹出int类型值,然后存到位置为1的局部变量中

JVM-内存模型 _ JavaClub全栈架构师技术笔记

 

 

   第三行与第四行跟上面一样,不在赘述。执行第三行与第四行后结果为:

  JVM-内存模型 _ JavaClub全栈架构师技术笔记

 

 

   继续看第五行&第六行&第七行:

    4: iload_1 - 从局部变量1中装载int类型值入栈(将下标为1的变量值压入操作数栈)

    5: iload_2 - 从局部变量2中装载int类型值入栈(将下标为2的变量值压入操作数栈)

    6: iadd - 将栈顶两int类型数相加,结果入栈(将操作数栈的两个int相加,然后重新压入操作数栈)

 

 JVM-内存模型 _ JavaClub全栈架构师技术笔记

 

 

 

  继续看第八行&第九行:

    7: iconst_2 - 将int类型值2压入栈 (把下标为2的变量值压入操作数栈)

    8: imul - 将栈顶两int类型数相乘,结果入栈(把操作数栈里面的两个数相乘所得结果压入操作数栈)

JVM-内存模型 _ JavaClub全栈架构师技术笔记

 

 

   继续看第十行:

    9: istore_3 - 将栈顶int类型值保存到局部变量3中(将操作数栈里面的6出栈到c的内存区域中)

JVM-内存模型 _ JavaClub全栈架构师技术笔记

 

 

  第十一行:

    10: iload_3 - 从局部变量3中装载int类型值入栈(把c的值压到操作数栈)

    11: ireturn - 返回int类型值(把操作数栈的int类型值返回)

JVM-内存模型 _ JavaClub全栈架构师技术笔记

   从上面对add方法的反编译指令码可以很好的看出一个方法执行过程,这个时候我们再来看下栈帧中的四个概念:

    1-局部变量表:存放方法内部定义的局部变量内存空间

    2-操作数栈:操作数的临时存放内存区域

    3-动态链接:==================================这里先空着,等会解释

    4-方法出口:方法的指令出口(记录上层方法执行指令码的位置)

  这里插入程序计数器和本地方法栈:从上面一系列图可以看出,栈和程序计数器和本地方法栈的颜色是一致的,所以说,这两快空间也是跟线程挂钩的

    程序计数器:记录jvm执行指令码的行数(Stack.txt的方法Code中行数),随着程序执行一直变动。

    本地方法栈,记录当前线程调用本地方法(native修饰的方法)中的所产生内存的容器。

  看下图:

JVM-内存模型 _ JavaClub全栈架构师技术笔记

 

  到这里,栈基本结束了,顺便把程序计数器和本地方法栈带上了

  问题1:栈会在什么情况下会溢出?

    方法嵌套调用的时候占用内存超过栈的默认大小的时候会出现栈溢出的情况。

  问题2:栈的默认大小是多大,如何修改?

    栈的默认大小为1024KB(一个线程栈给的空间),修改命令为:-Xss128k

  问题3:栈的修改会发生什么情况?

    如果把栈默认大小调小,在栈总容量不变的情况下,可以执行更多的线程,同理,调大反之。

 

  ========================我是分割线===========================

 

  下面我们来看方法区

    方法区:主要存放常量&静态变量&类元信息(类编译好以后的组成部分,成员变量&方法&修饰符等等;即存放类中符号引用=>常量池中)---(可以看下面代码)

  上面说到类元信息,我们可以再次反编译Stack.class看看,我们到clas目录下, 执行命令javap -v Stack.class > Stack.txt;我们打开txt:

Classfile /D:/work/mycode/cloud-parent/app-user/target/classes/com/ghsy/user/test/Stack.class
  Last modified 2020-7-27; size 588 bytes
  MD5 checksum 463b382878dd7e8e370be348be2980b6
  Compiled from "Stack.java"
public class com.ghsy.user.test.Stack
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #5.#26         // java/lang/Object."<init>":()V
   #2 = Class              #27            // com/ghsy/user/test/Stack
   #3 = Methodref          #2.#26         // com/ghsy/user/test/Stack."<init>":()V
   #4 = Methodref          #2.#28         // com/ghsy/user/test/Stack.add:()I
   #5 = Class              #29            // java/lang/Object
   #6 = Utf8               <init>
   #7 = Utf8               ()V
   #8 = Utf8               Code
   #9 = Utf8               LineNumberTable
  #10 = Utf8               LocalVariableTable
  #11 = Utf8               this
  #12 = Utf8               Lcom/ghsy/user/test/Stack;
  #13 = Utf8               add
  #14 = Utf8               ()I
  #15 = Utf8               a
  #16 = Utf8               I
  #17 = Utf8               b
  #18 = Utf8               c
  #19 = Utf8               main
  #20 = Utf8               ([Ljava/lang/String;)V
  #21 = Utf8               args
  #22 = Utf8               [Ljava/lang/String;
  #23 = Utf8               stack
  #24 = Utf8               SourceFile
  #25 = Utf8               Stack.java
  #26 = NameAndType        #6:#7          // "<init>":()V
  #27 = Utf8               com/ghsy/user/test/Stack
  #28 = NameAndType        #13:#14        // add:()I
  #29 = Utf8               java/lang/Object
{
  public com.ghsy.user.test.Stack();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 3: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/ghsy/user/test/Stack;

  public int add();
    descriptor: ()I
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=4, args_size=1
         0: iconst_1
         1: istore_1
         2: iconst_2
         3: istore_2
         4: iload_1
         5: iload_2
         6: iadd
         7: iconst_2
         8: imul
         9: istore_3
        10: iload_3
        11: ireturn
      LineNumberTable:
        line 7: 0
        line 8: 2
        line 9: 4
        line 10: 10
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      12     0  this   Lcom/ghsy/user/test/Stack;
            2      10     1     a   I
            4       8     2     b   I
           10       2     3     c   I

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=2, args_size=1
         0: new           #2                  // class com/ghsy/user/test/Stack
         3: dup
         4: invokespecial #3                  // Method "<init>":()V
         7: astore_1
         8: aload_1
         9: invokevirtual #4                  // Method add:()I
        12: pop
        13: return
      LineNumberTable:
        line 16: 0
        line 18: 8
        line 19: 13
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      14     0  args   [Ljava/lang/String;
            8       6     1 stack   Lcom/ghsy/user/test/Stack;
}
SourceFile: "Stack.java"

  上面的存放的就是Stack的类元信息。

  我们看main反法,调用add方法处,我们把这个 (4: invokespecial  #4)指向了常量池中的是#4,我们再去常量池看#4,就是method add(),#4中又包含#2和#26,

    继续去找#2和#26分别对应为stack对象和add ();其他代码同理

  上述过程中,把一个个符号引用转为具体的引用过程(存放符号引用具体指令码的入口地址),就是上面缺失的动态链接,同理,上一篇类加载机制中的静态链接也一样,

    只是两者区别在于静态链接是类加载的时候完成,动态链接是程序运行期间完成。如果用图来表示的话,那应该就是下图:

JVM-内存模型 _ JavaClub全栈架构师技术笔记

 

   那么问题来了

  问题4:类元信息是什么

    存放的是xxx.class编译后的指令码

  问题5:什么会被放入到方法区

    常量:final修饰的变量;静态变量:static修饰的变量;类元信息:类编译过后的指令码

 

  ==================我是分割线====================

  

  下面我们来看堆:

JVM-内存模型 _ JavaClub全栈架构师技术笔记

 

 

  存放的都是对象内存地址,里面可以分为两大快,一个为老年代(默认站堆内存大小为2/3);一个为年轻代默认站堆内存大小为1/3)

  年轻代又可以分为一个Eden区(默认占年轻代的8/10)和连个Survivor区(默认占年轻代的1/10)

  年轻代:新生成的对象存放区域。

  老年代:大对象;默认15次minor gc存活的对象;动态年龄判断可以直接进入老年代的对象;达不到15次minor gc,但是由于gc过后Survivor区存放不下的对象(这块下一篇具体展开来讲)

 

  5个区域都差不多了,那么我们5个区域是什么关系呢?我们现在串起来看看:

JVM-内存模型 _ JavaClub全栈架构师技术笔记

 

 

  以上都是比较粗略的内存模型,自己刚刚学习到这,做个简单记录。

  

  生而为人,我很抱歉

来源链接:https://www.cnblogs.com/ghsy/p/13387005.html

版权声明:
1、JavaClub(https://www.javaclub.cn)以学习交流为目的,由作者投稿、网友推荐和小编整理收藏优秀的IT技术及相关内容,包括但不限于文字、图片、音频、视频、软件、程序等,其均来自互联网,本站不享有版权,版权归原作者所有。

2、本站提供的内容仅用于个人学习、研究或欣赏,以及其他非商业性或非盈利性用途,但同时应遵守著作权法及其他相关法律的规定,不得侵犯相关权利人及本网站的合法权利。
3、本网站内容原作者如不愿意在本网站刊登内容,请及时通知本站(javaclubcn@163.com),我们将第一时间核实后及时予以删除。


本文链接:https://www.javaclub.cn/java/42619.html

分享给朋友:

“JVM-内存模型” 的相关文章

浅谈JVM内存模型

浅谈JVM内存模型

JAVA虚拟机在执行JAVA程序的时候,会把它管理的内存分成若干不同的数据区域,每个区域都有各自的用途。目前大致把JVM内存模型划分为五个区域:程序计数器,虚拟机栈,本地方法栈,堆和方法区。   程序计数器 程序计数器(ProgramCounterR...

深入理解JVM(1)——JVM内存模型

Java虚拟机的内存空间分为五个部分,分别是: 程序计数器; Java虚拟机栈 本地方法栈 堆 方法区 接下来对这五部分分别进行详细的介绍 1、程序计数器:   a)什么是程序计数器:程序计数器是内存中的一个很小...

JVM学习笔记1:Java虚拟机内存模型

JVM学习笔记1:Java虚拟机内存模型

JVM学习笔记1:Java虚拟机内存模型 学习JVM,Java虚拟机对理解Java程序执行过程和Java程序性能调优具有很大帮助。本系列博客旨在由浅到深学习并理解JVM。参考阅读:《深入理解Java虚拟机-JVM高级特性和最佳实践》。这个书写的非常好,推荐有条件的读者买一本来阅读...

jvm-java 内存模型  以及各个分区具体内容

jvm-java 内存模型 以及各个分区具体内容

java内存模型,这里其实是指 jvm运行时内存模型 1 每一个应用程序,都有一个JVM 而不是 多个应用程序,共享一个jvm 2 java源文件,首先通过编译器,把java语法的代码,编译成 jvm语法的字节码文件 这个过程,是不涉及到...

java jvm设置

2. 如何分配JVM内存设置: (1)当在命令提示符下启动并使用JVM时(只对当前运行的类Test生效):     java -Xmx128m -Xms64m -Xmn32m -Xss16m Test     (2)当在集...

jvisualVm监控远程的jvm

jvisualVm监控远程的jvm

jvisualVm是Netbeans的profile子项目,已在JDK6.0 update 7 中自带(java启动时不需要特定参数,监控工具在bin/jvisualvm.exe),能够监控线程,内存情况,查看方法的CPU时间和内存中的对 象,已被GC的...

JVM 内存模型

JVM 内存模型

Java 的内存模型JMM(Java Memory Model) JMM主要是为了规定了线程和内存之间的一些关系。根据JMM的设计,系统存在一个主内存(Main Memory),Java中所有实例变量都储存在主存中,对于所有线程都是共享的。每条线程都有自己的工作内存(Workin...

浅析JVM(一)

浅析JVM(一)

算是对自己最近这段时间学习JVM的一个总结吧。 不说废话,开整!   一、Java虚拟机的内存模型 首先,我们创建一个简单类: public class HelloWorld { public static void main(Stri...

线上jvm 内存飙高排查

线上jvm 内存飙高排查

1.jps查看java进程的pid 2.使用jmap把内存导出,查看是哪些对象占用内存高 jmap -histo 16352 >f:/dev/histo.txt 3. 使用jmap查看堆内存的使用情况 jmap -heap 16...

二: Jvm内存模型

二: Jvm内存模型

为什么jvm要有内存模型   在  上一章节  我们清楚代码的运行流程之后,那么下面一段代码我们就可以知道: 1. main线程启动,main()方法的栈帧压入main线程的虚拟机栈2. web()方法的栈帧也压入main线程的虚拟机栈3. web()栈...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。