JVM内存结构

参考文档:
java直接内存原理
JVM内存结构
JVM内存结构
JVM内存结构

JVM内存结构

JVM运行时内存

  • 程序计数器:
    • 程序计数器是一块很小的内存,它存储的是当前线程运行的字节码的行号,它在分配的时候是线程独享的,使用的时候是线程共享的,它的生命周期和线程的生命周期一致。
    • 正常单线程程序计数器是没有必要出现的,它的出现和JVM的cpu时间片切换运行方式有关系,多线程时每个线程得到cpu运行的一个时间片,当时间片结束,程序还在没有结束的时候,线程回挂起,线程再次运行的时候就可以根据程序计数器来继续运行。
    • JVM分配管理,此区域不会出现内存溢出。
  • Java虚拟机栈:
    • java虚拟机栈在线程建立的时候分配的,生命周期和线程相同。
    • java虚拟机栈中存在一种结构栈帧,一个方法对应一个栈帧,栈帧存储的时局部变量表、操作数栈、动态链接、方法出口。局部变量表就是我们通常所说的“栈内存”,它的空间大小是在编译期间分配的固定大小,它存储的是编译时已知的基本数据类型和对象引用类型。
    • Java虚拟机栈分配:
      • 固定大小:当栈深度超过JVM固定大小,StackOverFlowError。
      • 动态扩展:当栈动态扩展超过本地内存限定,OutOfMemoryError。
  • 本地方法栈:
    • 本地方法栈和Java虚拟机栈一样,区别在于本地方法栈服务员native方法,另外在HotSpot虚拟机中这两个是合在一起的。
  • 堆:
    • JVM管理的最大一块内存区域,属于线程共享的,是垃圾收集器最主要管理的内存区域,也称为“CG堆”。
    • 堆存储的是对象实例,采用的是分代管理的垃圾回收机制,分为新生代、老年代、永久代(部分虚拟机没有)。
    • 堆分配的内存空间可以不是连续的内存,在虚拟机启动的时候分配,它的大小可以通过-Xmx -Xms来改变堆的大小,堆的内存空间不够的时候会抛出OutOfMemoryError。(章内容缺垃圾收机制没有补全)
  • 方法区:
    • 方法区逻辑上属于堆,在HotSpot虚拟机中方法区位于永久代,但是在其他虚拟机中不存在永久代。
    • 方法区存储的是类信息(类版本、字段、接口、方法)、常量、静态变量、即时编译器编译后的代码等。(文章内容缺垃圾收机制没有补全)
    • 方法区在虚拟机启动的时候分配内存。从内存分配,当内存不足分配时,提示OutOfMemoryError。
  • 运行时常量池:
    • 运行时常量池属于方法区的一部分。
    • 在class文件中存在一个常量池,编译的时候将字面量和符号引用存储在这个常量池中。在类加载的时候这个常量池中的内容会进入运行时常量池中,另外运行时常量池还存储运行时产生的常量,如String()、Intern()方法。

直接内存

  • 可以使用native方法直接分配直接内存,然后使用存储在java堆中DirecByteBuffer对象作为这块内存的引用进行操作。直接内存大小不会受堆大小的限制,但是收到本地内存和处理器寻址空间限制。-Xmx设置不合理也会出现OutOfMemoryError。

关于JVM内存结构问答

  • (1)JVM管理的内存结构是怎样的?
    JVM管理的内存结构由PC寄存器、java虚拟机栈、本地方法栈、堆、方法区、运行时常量池构成。
  • (2)不同的虚拟机实现运行时内存池有什么不同?
    《Java虚拟机规范》定义了JVM运行时内存结构,该规范对内存的定义比较宽松的,特别时方法区。方法区定义是逻辑上属于堆,不同厂商实现也不尽相同,在HotSpot虚拟机中,HotSpot将方法区置于堆的永久代中,到了java8取消了永久代,使用本地内存来存储元数据信息,称为元空间。
  • (3)运行时内存哪些是线程独享,哪些是线程共享的?
    程序计数器分配是线程独享的,使用是线程共享的;Java虚拟机栈和本地方法栈是线程独享的;堆和方法区是线程共享的。
  • (4)出来java运行时内存空间外,还有哪些内存可以使用?
    直接内存,可以由native方法分配内存空间,通过JVM堆中的DirecByteBuffer对象作为这块内存的引用进行操作。
  • (5)堆和栈的区别?
    • 堆:线程共享、JVM启动时分配、存储实例
    • 栈:线程独享、线程启动的分配、存储局部变量表,操作数,动态链接,方法出口、
  • (6)Java数组存储在堆里面。
  • (7)Java对象创建有哪几种方式?
    • 方法一:
      • User user = new User()
    • 方法二:
      • User user = User.class.newInstance();
    • 方法三:
      • Constructor constructor = User.getConstructor();
      • User user = constructor.newInstance();
    • 其他:还可以使用反序列化和Clone方法。
  • (8)Java创建对象的过程?
    • a 到常量池中定位类的符号引用
    • b 查看引用所代表的类是否被加载、解析、初始化过,没有,则加载类
    • c 为类从堆分配内存空间,指针碰撞(内存规整)或空闲列表(内存交错)
    • d 类空间全部赋值为零
    • e 对对象进行必要设置
    • f 使用构造函数初始化类
  • (9)java对象一定在堆中么?
    • 逃逸分析。如果一个对象逃逸出方法,则采用栈上分配。
  • (10)如何获取堆和栈的快照信息
    • 使用服务器上的jMap命令获取堆的快照信息,使用jStack命令来获取栈快照信息。

内存结构代码参考

编译器编译后的字节码类文件:
1Cow.class
//
//编译器在外部类添加了静态方法 Cow.access$0(Cow arg0)。它将返回作为参数传递给它的对象的私有域weight。
//如果内部类不访问外部类的私有字段,将不会在外部类中添加静态方法Cow.access$0(Cow arg0)。

public class Cow {
    private double weight;

    public Cow() {
    }

    public Cow(double weight) {
        this.weight = weight;
    }

    public void test() {
        //编译器将CowLeg cl = new CowLeg(1.12, "黑白相间");语句编译为
        CowLeg cl = new CowLeg(this, 1.12D, "黑白相间");
        cl.info();
    }

    //编译器在外部类添加了静态方法
    static double access$0(Cow arg0){
        return arg0.weight;
    }

    public static void main(String[] args) {
        Cow cow = new Cow(378.9D);
        cow.test();
    }
}
//编译器为了在内部类的实例中引用外部类的实例对象,必添加一个附加的实例域Cow this$0(this$0名字是由编译器合成的,在自编写的代码中不应该引用它,因为合成名称可能不同)。
//另外,编译器修改了所有的内部类的构造器,添加了一个引用外部类实例的参数Cow arg0。
//不管内部类是否访问外部类,内部类的构造器是一样的,均有Cow arg0参数。

class Cow$CowLeg {
    private double length;
    private String color;
    //编译器必添加一个附加的实例域Cow this$0
    final Cow this$0;

    //编译器在内部类的构造方法中,必添加一个引用外部类实例的形参Cow arg0
    public Cow$CowLeg(Cow arg0) {
        this.this$0 = arg0;
    }

    public Cow$CowLeg(Cow arg0, double length, String color) {
        this.this$0 = arg0;
        this.length = length;
        this.color = color;
    }

    public void setLength(double length) {
        this.length = length;
    }

    public double getLength() {
        return this.length;
    }

    public void setColor(String color) {
        this.color = color;
    }

    public String getColor() {
        return this.color;
    }

    public void info() {
        System.out.println("当前牛腿颜色是:" + this.color + ", 高:" + this.length);
        System.out.println("本牛腿所在奶牛重:" + Cow.access$0(this.this$0));
    }
}

在这里插入图片描述

参考文档:
java直接内存原理
JVM内存结构
JVM内存结构
JVM内存结构

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值