基本类型与引用类型的本质区别

    xiaoxiao2023-09-30  164

     

    通过这篇文章你将了解堆、栈、方法区等以及8种基本类型与引用类型在内存中的区别

    栈区:

    栈中只存储方法中(不包括对象的成员变量)的基础数据类型引用数据类型地址,对象都存放在堆区中,每个栈中的数据(原始类型和对象引用)都是私有的,其他栈不能访问。栈分为3个部分:基本类型变量区、执行环境上下文、操作指令区(存放操作指令)。

    堆区:

    存储的全部是对象实例,每个对象都包含一个与之对应的class的信息(class信息存放在方法区)。jvm只有一个堆区(heap)被所有线程共享,堆中不存放基本类型和对象引用,只存放对象本身,几乎所有的对象实例和数组都在堆中分配。

    方法区:

    又叫静态区,跟堆一样,被所有的线程共享。它用于存储已经被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。。

    常量池:常量池(constant pool)属于方法区的一部分,指的是在编译期被确定,并被保存在已编译的.class文件中的一些数据。它包括了关于类、方法、接口等中的常量,也包括字符串常量(现在是存放在堆中),多个相同的内容在常量池中只存放一份。

    内存堆和栈的区别:堆一般用户存储可变长度的数据,如字符串类型;而栈用于存储固定长度的数据,如整型int。堆的读写速度远不及栈,因为堆是程序在运行是动态分配内存,而栈在程序编译时便已经分配好内存,但栈的大小不如堆,所以常出现栈溢出而很少出现堆溢出。

    JAVA各个区线程共享图

    问题一:

    有如下代码:

    String s1 = "abc";

    String s2 = "abc";

    String s3 = new String("abc");

    String s4 = new String("abc");

    System.out.println(s1 == s2) ;//true

    System.out.println(s3 == s4); //false

    System.out.println(s1 == s3); //false

    为什么 s1==s2是ture而s3==s4与s1==s3却是false呢?

    原因:

     其实java中有一个“字符数据池”的内存管理机制。

    当执行String str="abc"这句话时,会先去“字符数据池”搜索时候有“abc”这个字符串,如果有,

    则将字符串的首地址赋值给str,如果没有,生成一个新的字符串“abc”并且将首地址赋值给str;

    而执行 String str=new String("abc") 这句话时,不会考虑时候已经存在了“abc”这个字符串,而

    是直接生成一个新的字符串“abc”并将首地址赋值给str,注意“abc”并不放在“字符数据池”中;

    所以s2的地址其实是由s1给的,而s3,s4是分别new出来的两个不同地址。

    问题二:

    有如下代码:

            int a1=127;         int a2=127;         Integer a3=127;         Integer a4=127;         Integer a5 = 128;         Integer a6 = 128;         Integer a7 = new Integer(127);         Integer a8 = new Integer(127);         System.out.println(a1 == a2);//true         System.out.println(a3 == a4);//true         System.out.println(a5 == a6);//false         System.out.println(a7 == a8);//false         System.out.println(a1 == a3);//true         System.out.println(a3 == a7);//false

    为什么a3 == a4是true而 a5 == a6却是false呢?

    主要原因在JAVA中的Integer方法中如果值在-128-127范围内会将这个值进行缓存,下一次访问它因为127已经存在,所以会将s3的地址直接指向s4。

             static final int low = -128;         static final int high = 127;

    public static Integer valueOf(int i) {         if (i >= IntegerCache.low && i <= IntegerCache.high)             return IntegerCache.cache[i + (-IntegerCache.low)];         return new Integer(i);     }

     

    引用类型访问对象的两种方式:句柄访问方式和直接指针

    1. 如果使用句柄访问方式,Java堆中将会划分出一块内存来作为句柄池,reference中存储的就是对象的地址,而句柄中包含了对象实例数据和类型数据各自的具体地址信息。

    2. 如果通过直接指针方式访问,Java堆对象的布局中就必须考虑如何放置访问类型数据的相关信息,reference中直接存储的就是对象的地址。

    参考:https://blog.csdn.net/zhangsanfeng2009/article/details/80900963

               https://blog.csdn.net/qq_34467922/article/details/80790443

               https://blog.csdn.net/OyangYujun/article/details/41173747

     

    最新回复(0)