zoukankan      html  css  js  c++  java
  • java对象的内存

    对象创建时内存分配

    JMM描述我们了解到https://www.cnblogs.com/dhcao/p/10897256.html

    • 从归属区分:

      • 归属线程的:虚拟机栈、本地方法栈、pc计数器
      • 归属jvm的:堆、方法区
    • 从功能区分:

      • 保存对象实例数据:堆
      • 保存类的数据:方法区
      • 保存方法变量:虚拟机栈
      • 保存本地方法变量:本地方法栈
      • 保存线程执行位置:pc计数器
    • ps:jdk8以前,HotSpot通常用永久代来作为方法区的实现,其内存大小在启动时确定,虽然gc会处理这里的垃圾,但是当加载过多的类时,还是会出现oom。

      在jdk8以后的版本中,元空间取代了永久代,元空间开辟在本地内存(不在虚拟机中,直接使用服务器内存),理论上不会出现内存不足,并且将原先永久带中的字符串常量移到堆中,其他元数据(类元、字段、静态属性、方法、常量等)移动到元空间。

    1 内存管理

    new:我们很多时候,使用new来创建对象,new是强类型校验,他能调动所有的构造函数,当机器遇到new指令的时候,先检查类是否已经被加载,如果没有,那么先进行类加载。加载完毕,则开始在堆中分配内存。

    ​ 对象的内存我理解这里分为三个部分(也可将后两者放一起,毕竟都在堆中,也都属于对象):

    • 在方法区中分配对象元数据,类元、字段、静态属性、方法、常量等
    • 在堆中为对象分配对象头信息内存
    • 初始化对象在堆中分配对象的实例数据内存。

    2 类加载时分配对象元数据区

    ​ 在java8之后,元空间取代perm(永久代)作为元数据所在地,并将常量池移动到堆中。

    • 在类加载过程“加载”阶段,虚拟机将class文件用二进制流方式读取到方法区
    • 在方法区中生成Class对象以保存类信息
    • 分配类变量(与之相比较的叫实例变量)内存,static修饰的变量、final域中的变量赋值、字段表、常量池等等。

    注:由于数组时没有对象信息的,所以无法通过类加载器来加载数组(类加载器通过加载class文件获得类信息),所以数组是由虚拟机而不是类加载器创建的。

    数组是一串连续的内存地址....大小一旦分配就无法更改。

    3 对象头信息数据区

    ​ 对象保存在堆中,对象头信息包括2部分:对象标记(Mark Word) 和 类型指针

    • 该数据一定是在堆中,属于对象的。

    • 对象的运行时信息,记做Mark Word。

    • 此对象头信息记录如下类型

      存储内容 偏向锁标志位(biased_lock 标志位(lock) 状态
      对象哈希码、对象分代年龄 0 01 未锁定
      指向锁记录的指针 0 00 轻量级锁定
      指向重量级锁的指针 0 10 膨胀(重量级锁定)
      空,不需要记录信息 0 11 GC标记
      偏向线程ID、偏向时间戳、对象分代年龄 1 01 偏向锁

      其一是对象的各种记录:

      所谓运行时信息,值的是这个对象在运行过程中需要保存的信息,既然是运行时才记录,说明随着对象的运行而在不断变化。

      例如:对象分代年龄,对象每在一次Minor GC中存活,将年龄增加1,并在适当时候(默认年龄15,可设置)移动到“老年代”。

      其二是对象的锁状态:

      偏向锁标志位(biased_lock)+ 标志位(lock)决定了对象的锁状态。

      • 该部分内存在32位和64位系统中,分别占用32bit和64bit内存
      • 32位系统中:
        • 25bit储存对象哈希码(identity_hashcode
        • 4bit储存对象年龄分代(age
        • 1bit储存偏向锁标志位(biased_lock
        • 2bit储存锁标志位(lock
      |-------------------------------------------------------|--------------------|
      |                  Mark Word (32 bits)                  |       State        |
      |-------------------------------------------------------|--------------------|
      | identity_hashcode:25 | age:4 | biased_lock:1 | lock:2 |       Normal       |
      |-------------------------------------------------------|--------------------|
      |  thread:23 | epoch:2 | age:4 | biased_lock:1 | lock:2 |       Biased       |
      |-------------------------------------------------------|--------------------|
      |               ptr_to_lock_record:30          | lock:2 | Lightweight Locked |
      |-------------------------------------------------------|--------------------|
      |               ptr_to_heavyweight_monitor:30  | lock:2 | Heavyweight Locked |
      |-------------------------------------------------------|--------------------|
      |                                              | lock:2 |    Marked for GC   |
      |-------------------------------------------------------|--------------------|
      
      • 64位系统中

    • 关于类型指针(class pointer)

      • 这部分占用4个字节(如果不开启指针压缩,则占8个字节,此处取4个字节)

      明显就是在对象头中指向方法区中class元数据的指针,通过此指针,我们可以通过对象来获取到对象的class信息。毕竟class是在方法区中,而对象是在堆中,如果没有指针关联是不科学的,而且由于一个class可以有多个对象实例,所以肯定是由对象指向class

    这里是不是很明显,如何计算一个对象的大小??

    在64位机器上,对象头信息包含64bit(8个字节)的对象运行时信息(Mark Word ),还有类型指针(class pointer)4个字节。那么就算没有任何实例需要赋值,一个对象的创建最少需要12个字节但由于HotSpot VM要求对象初始化大小必须是8的倍数,所以实际需要16个字节

    4 对象的实例数据区

    ​ 在类加载“初始化”后,如果对象需要初始化赋值,那么需要执行init方法,该方法将为对象赋值实例数据

    • 该内存一定是在堆内存区,也是属于对象的。
    • 如果对象是引用,那么只需要保存实例的引用,一个引用占用4个字节。
    • 如果是基本类型,则根据对象类型保存对应长度

    5 填充区域

    ​ 由于HotSpot VM要求对象初始化大小必须是8的倍数,所以有这个要求而已。

    6 图解对象内存分配

    7 实例:计算对象内存大小

    /**
     * @Author: dhcao
     * @Version: 1.0
     */
    public class OneObj {
    
        // 1. 对象头占用12个字节
    
        // 2. 这个在方法区,不包含在对象中,占0个字节
        private static int a = 10;
    
        // 3. 3个int共占12个字节
        int a1;
        int a2;
        int a3;
    
        // 4. 2个refObj共占8个字节
        Object b1;
        Object b2;
    
        // 5. 此处也只算引用,只占8个字节。ps:一个Integer对象大小是16个字节...
        Integer o1 = new Integer(91);
        Integer o2 = new Integer(98);
    
        // 所以共占:12 + 12 + 8 + 8 = 40 。正好是8的倍数,所以就占40个字节。
    }
    
    
    
    // 测试
    /**
     * @Author: dhcao
     * @Version: 1.0
     */
    public class SizeTest {
        public static void main(String[] args) {
            OneObj obj = new OneObj();
            while(true){
    
            }
        }
    }
    
    • 按照我们的预想,这个是40个字节没问题了,我启动jprofile监控看看就知道了

    • 如果我们增加一个对象呢
     		// 5. 此处也只算引用,占了12个字节。ps:一个Integer对象大小是16个字节...
                    Integer o1 = new Integer(91);
                    Integer o2 = new Integer(98);
    		Integer o3 = new Integer(98);
    	
    		// 此时比上面测试多了一个引用o3,那么应该是44个字节,进行补齐,应为48字节
    

  • 相关阅读:
    Angular 学习笔记 (消毒 sanitizer)
    资源链接
    QM作品
    读书笔记
    javascript jQuery遇到的小问题 不定添加
    css 平时遇见CSS的一些小技巧 不定添加
    html 小却重要的问题 不定添加
    Array 对象
    test
    Javascript定义类(class)的三种方法
  • 原文地址:https://www.cnblogs.com/dhcao/p/11908902.html
Copyright © 2011-2022 走看看