1.1.Java内存区域

Java内存区域

1.前提概要

​ 在虚拟机自动内存管理机制下,Java不像C、C++程序开发那样需要手动开辟释放内存空间。不容易出现内存溢出(Out Of Memory)、内存泄漏(Memory Leak)。但并不代表就不会出现,了解了Java的内存区域布局,就能更清晰的去定位问题和解决问题

2.运行时数据区域

​ 不同的数据区域的生命周期各不相同,有的随着JVM Process启动而一直存在,有的则是随着用户线程的启动和结束而建立和销毁。

​ 拓展:

​ 用户线程和守护线程

​ 用户线程:Thread.setDaemon(false)
​ 守护线程:Thread.setDaemon(true)
​ 线程创建出来默认为用户线程

​ 用户线程和守护线程的区别:

​ 1.只要有用户线程在运行,JVM就会一直存活

​ 2.如果没有用户线程,或者都是守护线程,那么JVM结束(所有的线程都会结束)

​ 3.守护线程依赖操作系统

1.程序计数器

2.Java虚拟机栈

3.本地方法栈

4.Java堆

5.方法区

6.运行时常量池

7.直接内存

一图概览(图片为转载,如有侵权,请联系)

在这里插入图片描述

1.程序计数器

​ 它是当前线程执行的字节码行号指示器。它是程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等操作都需要依赖这个计数器来完成。它是线程私有的。此内存区域不存在OOM情况。

​ ①线程执行Java方法,则计数器记录的是正在执行的虚拟机字节码指令的地址

​ ②线程执行本地(native)方法,则记录的为空(Undefined)

2.Java虚拟机栈

​ 它是线程私有的,和线程的生命周期相同。它描述的是Java方法执行的线程内存模式。每个方法被执行的时候,都会创建一个栈帧,用于存储局部变量表、操作数栈、动态连接、方法出口等信息。每一个方法从开始调用到执行结束的过程,就对应着一个栈帧从入栈到出栈的过程。

​ ①局部变量表

​ 用于存放编译期可知的JVM基本数据类型(8大基本数据类型)、对象引用(它并不是对象本身,可能是指向对象的地址引用指针,也可能是指向一个代表对象的句柄或者与对象位置相关联的信息)和returnAddress(指向了一条字节码指令的地址)

​ 局部变量表所需的内存空间是在编译器间完成分配的

​ ②操作数栈

​ 用于计算的临时数据存储区域,和局部变量表进行交互。它用来执行JVM指令,用来给局部变量表中的变量进行数据操作的,比如赋值、求和(iconst_0:将int类型常量压入操作数栈、istore_1:将int类型常量压入栈、imul、iadd、bipush等操作,这些JVM指令翻译成机器码由CPU去执行)。

​ ③动态连接

​ 在运行的过程中,把符号引用转换成方法对应的代码的直接引用

​ 编译期会把一些静态方法、变量加载出来,此为静态连接。实例方法不像静态方法是在程序加载的过程中解析的,而是在运行到该方法的时候进行解析的,运行态期间如果调用了实例方法,会把方法区存储的该实例方法的符号引用转变成直接引用

​ ④方法出口

​ 方法返回指令 : 执行引擎遇到一个方法返回的字节码指令,这时候有可能会有返回值传递给上层的方法调用者,这种退出方式称为正常完成出口。

异常退出 : 在方法执行过程中遇到了异常,并且没有处理这个异常,就会导致方法退出。

3.本地方法栈

​ 它和虚拟机栈类似,区别在于:虚拟机栈执行Java方法,本地方法栈执行本地(native)方法

4.Java堆

​ 它是JVM管理的最大的一块内存。它是线程共享的内存区域。它是用来存放对象实例。它是被垃圾收集器管理的内存区域。按照回收内存的角度来看,分为Eden、From Survior、To Survior、Old Gen,这些分区只是站在垃圾收集器的角度去划分的区域,并不是JVM划分的内存区域。

​ Java Heap 可以处于物理上不连续的内存空间,但是在逻辑上应当被视为连续的。对于一些大对象,例如数组,它就是连续的内存空间。它的大小是可以通过JVM参数来控制的(-Xmx、-Xms)

5.方法区

​ 它是线程共享的内存区域,用来存放JVM加载的类元信息、常量、静态变量、即使编译器编译后的代码缓存等数据。

​ JDK8以前方法区的实现是永久代,永久代并不等价于方法区。我的理解是方法区是一个接口,而永久代就是它的一种实现。

​ JDK7的时候就把原本存放在永久代的字符串常量池、静态变量等移到Java Heap中。

​ JDK8的时候用在直接内存实现的元空间(MetaSpace)来替代永久代,并且把一些其他的数据(类元信息)移到元空间中。

6.运行时常量池

​ 它是方法区的一部分,但是我浏览了一些网上的博客,发现有的也说JDK7以后运行时常量池和字符串常量池在Java Heap中。此处存疑。

​ Class文件除了类元信息(字段、方法、接口等描述信息),还有一项信息是常量池表(Constant Pool Table),用于存放编译期生成的字面量与符号引用,这些内容会在类加载后被放到运行时常量池中(下列反编译的代码中Constant pool的列表信息)。

  Last modified 2021-1-19; size 1026 bytes
  MD5 checksum ddd20c8bf14696f053b6798bbb50cc65
  Compiled from "Demo.java"
public class com.redis.Demo
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #13.#39        // java/lang/Object."<init>":()V
   #2 = Fieldref           #40.#41        // java/lang/System.out:Ljava/io/PrintStream;
   #3 = Class              #42            // java/lang/StringBuilder
   #4 = Methodref          #3.#39         // java/lang/StringBuilder."<init>":()V
   #5 = String             #43            // c =
   #6 = Methodref          #3.#44         // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   #7 = Methodref          #3.#45         // java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
   #8 = Methodref          #3.#46         // java/lang/StringBuilder.toString:()Ljava/lang/String;
   #9 = Methodref          #47.#48        // java/io/PrintStream.println:(Ljava/lang/String;)V
  #10 = Class              #49            // com/redis/Demo
  #11 = Methodref          #10.#39        // com/redis/Demo."<init>":()V
  #12 = Methodref          #10.#50        // com/redis/Demo.test:()V
  #13 = Class              #51            // java/lang/Object
  #14 = Utf8               STR
  #15 = Utf8               Ljava/lang/String;
  #16 = Utf8               ConstantValue
  #17 = String             #52            // str
  #18 = Utf8               name
  #19 = Utf8               <init>
  #20 = Utf8               ()V
  #21 = Utf8               Code
  #22 = Utf8               LineNumberTable
  #23 = Utf8               LocalVariableTable
  #24 = Utf8               this
  #25 = Utf8               Lcom/redis/Demo;
  #26 = Utf8               test
  #27 = Utf8               a
  #28 = Utf8               I
  #29 = Utf8               b
  #30 = Utf8               c
  #31 = Utf8               main
  #32 = Utf8               ([Ljava/lang/String;)V
  #33 = Utf8               args
  #34 = Utf8               [Ljava/lang/String;
  #35 = Utf8               demo
  #36 = Utf8               MethodParameters
  #37 = Utf8               SourceFile
  #38 = Utf8               Demo.java
  #39 = NameAndType        #19:#20        // "<init>":()V
  #40 = Class              #53            // java/lang/System
  #41 = NameAndType        #54:#55        // out:Ljava/io/PrintStream;
  #42 = Utf8               java/lang/StringBuilder
  #43 = Utf8               c =
  #44 = NameAndType        #56:#57        // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  #45 = NameAndType        #56:#58        // append:(I)Ljava/lang/StringBuilder;
  #46 = NameAndType        #59:#60        // toString:()Ljava/lang/String;
  #47 = Class              #61            // java/io/PrintStream
  #48 = NameAndType        #62:#63        // println:(Ljava/lang/String;)V
  #49 = Utf8               com/redis/Demo
  #50 = NameAndType        #26:#20        // test:()V
  #51 = Utf8               java/lang/Object
  #52 = Utf8               str
  #53 = Utf8               java/lang/System
  #54 = Utf8               out
  #55 = Utf8               Ljava/io/PrintStream;
  #56 = Utf8               append
  #57 = Utf8               (Ljava/lang/String;)Ljava/lang/StringBuilder;
  #58 = Utf8               (I)Ljava/lang/StringBuilder;
  #59 = Utf8               toString
  #60 = Utf8               ()Ljava/lang/String;
  #61 = Utf8               java/io/PrintStream
  #62 = Utf8               println
  #63 = Utf8               (Ljava/lang/String;)V
{
  public static final java.lang.String STR;
    descriptor: Ljava/lang/String;
    flags: ACC_PUBLIC, ACC_STATIC, ACC_FINAL
    ConstantValue: String str

  public com.redis.Demo();
    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 8: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/redis/Demo;

  public void test();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=3, 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: istore_3
         8: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
        11: new           #3                  // class java/lang/StringBuilder
        14: dup
        15: invokespecial #4                  // Method java/lang/StringBuilder."<init>":()V
        18: ldc           #5                  // String c =
        20: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        23: iload_3
        24: invokevirtual #7                  // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
        27: invokevirtual #8                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        30: invokevirtual #9                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        33: return
      LineNumberTable:
        line 15: 0
        line 16: 2
        line 17: 4
        line 18: 8
        line 19: 33
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      34     0  this   Lcom/redis/Demo;
            2      32     1     a   I
            4      30     2     b   I
            8      26     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           #10                 // class com/redis/Demo
         3: dup
         4: invokespecial #11                 // Method "<init>":()V
         7: astore_1
         8: aload_1
         9: invokevirtual #12                 // Method test:()V
        12: return
      LineNumberTable:
        line 22: 0
        line 23: 8
        line 24: 12
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      13     0  args   [Ljava/lang/String;
            8       5     1  demo   Lcom/redis/Demo;
    MethodParameters:
      Name                           Flags
      args
}
SourceFile: "Demo.java"

​ 运行时常量池相比Class类文件常量池具有动态特性,运行期间也可以将新的常量放入池中,比如String类的intern()方法。

​ 运行时常量池也会OOM。

7.直接内存

​ 可以理解成机器的物理内存,它不是JVM运行时数据区域的一部分,它不会受到Java Heap的大小限制,但它受限于本机物理内存的限制。也会发生OOM。

  • 6
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值