jvm专题 之 内存模型

文章目录

前言

  • 一个程序需要运行,需要在内存中开辟一块空间
  • 类是构建对象的模板,只有类加载到内存中才能创建对象

一个java对象的运行过程

1、我们自己创建一个Test类,功能只有一个:System.out.println("hello world");

2、然后通过终端运行 javac Test.java ,会将Test类编译成二进制字节码文件Test.class

3、然后我们继续运行 java Test.class ,于是会打印出"hello world"。

4、控制着程序的执行流程的是虚拟机栈

( 打印的过程就是在内存中进行的)

jvm内存分布

  • 由上边图可以看到,jvm主要分为五个区域。我们可以分为两大类: 线程独享:程序计数器、本地方法栈、虚拟机栈

    线程共享:堆、方法区

  • 每个区域主要的职责,参看上图就可以了,这里不多数
    (各位读者,如果有了解本地方法栈的,欢迎留言哦)

程序的基本运行程序

在java中,万物皆可对象。那么,什么是对象呢,对象与类又是什么关系呢?

对象

什么是对象

  • 对象是堆内的一块内存空间,是由类构建出来的

对象的创建

一、类加载检查
  • 检查 能否在常量池中定位到一个类的符号引用,并检查这个符号引用代表的类是否被加载、解析和初始化
    • 如果类已经加载过了,会在方法区有一个类的class对象
    • 如果类没有加载过,需要先执行类的加载流程
二、对象内存分配
  • 类加载检查通过 后,接下来虚拟机为新生对象分配内存(大小在类加载完成后便可以完全确定)

    • 分配内存的方法
      • 指针碰撞法(假设java中内存是绝对规整的)(带Compact的收集器,如Serial ParkNew)
      • 空闲列表法(java堆中内存并不规整)(基于Mark-Sweep算法的收集器,如CMS)
    • 多线程时如何保证划分的空间可用? (1) 使用CAS配上失败重试的方式保证更新操作的原子性

      (2)本地线程分配缓冲TLAB:线程开启时,虚拟机为每个线程分配一块较大的空间,然后线程内部创建对象时从自己的空间分配

      (每个线程在java堆中预先分配内存TLAB,可以通过-XX:+/-UseTLAB参数来设定)

三、初始化零值

给实例对象的成员变量赋零值,如int型为0,引用类型为null。

这样在没有赋值的情况下,对象也可以使用

四、设置对象头
  • 对象头
    • Mark Word:HashCode、GC分代年龄、持有锁的状态、偏向线程ID、偏向时间戳
    • 类型指针:指向类元数据的指针(虚拟机通过这个指针来确定这个对象时哪个类的实例)
  • 实例数据:对象真正存储的有效信息(相同的宽度总是被分配到一起)
  • 对齐填充:jvm要求对象的大小必须是8字节的整数倍
五、执行初始化方法

如 构造方法

对象的访问定位

  • 直接指针:对象实例数据 + 对象类型数据的指针
    • 特点:java堆中存储 对象实例数据+对象类型数据的指针 放在一起
    • 优点:访问速度快
  • 句柄指针:(句柄池中)对象实例数据的指针 + 对象类型数据的指针;对象实例数据在堆中额外的位置
    • 特点:java堆中需要划分出一部分来作为句柄池,
    • 好处:有稳定的句柄池,在对象被移动时只会改变句柄池中的指针
    • 缺点:相比直接指针,访问速度慢

对象与类的关系

  • 类是构建对象的模板,类中有什么,对象当中有什么。

由类创建对象的顺序

  • 我们先来一个Person类,然后再来一个Test类用来测试,一起来看一下顺序

  • 代码

    java 复制代码
    public class Person {
        String name;
        Integer age;
        Character sex;
        public static void breath(){
            System.out.println("会呼吸");
        }
        public void speek(){
            System.out.println("可以说话");
        }
    }
    java 复制代码
    public class Test {
        public static void main(String[] args) {
            Person person1 = new Person();
            person1.name = "Lucy";
            person1.age = 20;
            person1.sex = 'F';
            System.out.println(person1.name+",年龄为:"+person1.age+",性别是:"+person1.sex);
            person1.breath();    // 也可以Person.breath();
            person1.speek();
            
            // person2是为了后续的内容讲解,此处先不用关注
            Person person2 = new Person();
            person2.name = "Lucy";
            person2.age = 20;
            person2.sex = 'F';
            person2.breath();
            person2.speek();
        }
    }

    备注:person2是为了对比,下边内容讲解会先忽略这里,只看person1

  • 内存图

    执行顺序:

    1、加载Test类信息到方法区中

    2、main()方法入栈

    3、加载Person类信息到方法区

    4、给对象person1的name赋值,name为String类型,person1对象中name指向的是一个引用地址,数据存放在方法区的字符串常量池中

    5、给对象person1的age赋值,age为Integer类型,当age取值为[-128,127]时,age指向的是数组中的某一个。如果超出范围,会创建新的Integer对象

    6、给对象的sex赋值,在堆中创建一个对象

    7、调用breath()方法

    8、调用speek()方法

  • 多说两句(以上边的person1和person2来比对)

    • 如果age取值在[-128,127]内,多个对象指向的是同一内存空间

      java 复制代码
      System.out.println(person1.age == person2.age);
      -------------
      true
    • 如果age取值不在[-128,127]之间,那么不同的对象的age需要在堆中额外创建对象

      java 复制代码
      person1.age = 200;
      person2.age = 200;
      System.out.println(person1.age == person2.age);
      --------运行结果---------
      false
    • 对于Character类,只要值相等,那么多个对象指向的是同一空间地址

      java 复制代码
      System.out.println(person1.sex == person2.sex);
      --------运行结果---------
      true

对象的创建

相关推荐
CopyLower3 分钟前
安全点的应用场景及其原理详解
jvm·安全
算法与编程之美2 小时前
通过两个类计算一个长方形的周长和面积
java·开发语言·javascript·jvm·servlet
椰椰椰耶3 小时前
【JVM】类加载机制
jvm
Lilixy.18233 小时前
【Java-JVM】
java·开发语言·jvm
生产队队长3 小时前
JVM(HotSpot):虚拟机栈(JVM Stacks)与本地方法栈(Native Method Stacks)
jvm
golove6663 小时前
JVM 的性能指标监测
jvm
一休哥助手3 小时前
JVM中的GC流程与对象晋升机制详解
jvm
芜婳4 小时前
JVM 内存模型
java·开发语言·jvm
wht65874 小时前
Linux--基本指令
linux·运维·服务器·c语言·开发语言·jvm·c++