Java中new一个对象到底经历了什么?我们从内存的方面来分析它们.String定义的两种方式内存怎么安排的

一、前提知识

jvm的内存区域组成

Java把内存分为两种: 1: 栈内存,  2: 堆内存

1: 在函数定义的基本类型变量对象的引用变量都在函数的栈内存中分配

2: 堆内存用来存放由new创建的对象数组以及对象的实例变量

堆和栈的优缺点

1: 堆的优势是: 可以动态分配内存大小,生存期也不必事先告诉编译器,因为它是在运行是动态分配内存的.

缺点: 就是要在运行时动态分配内存, 存取速度较慢,

2; 栈的优势是: 存取速度比堆要快,仅次于直接位于CPU中的寄存器. 栈中数据也可以共享

缺点: 存在栈中的数据大小与生存期必须是确定的,缺乏灵活性.

 栈中有一个很重要的特殊性, 就是存在栈中的数据可以共享

例如

int a = 3;
int b = 3;

编译器处理int a = 3时,首先它会在栈中创建一个变量为a的引用, 然后查找有没有字面值为3的地址, 没找到就开避一个存放3这个字面值的地址, 然后将a指向3的地址.

接着处理int b = 3;跟a是一样的(这时候就会出现a与b同时指向3的情况). 如果后面要改变a的值为4只是将a的引用指向字面值为4的地址即可.

二、创建对象的内存分配情况

在Java中创建一个对象包括对象的声明实例化两步

以Student student = new Student("小明", 18);为例

2.1、对象的声明

 1: 声明一个对象student时,将在栈内存为对象的引用变量student分配内存空间, 但student值为空, 称student是一个空对象. 空对象不能够使用, 因为它还没有引用任何实体.

2.2、对象的实例化

对象实例化时的内存模型

执行new Student("小明", 18);

2.1: 在堆内存中为类的成员变量width, height分配内存, 并将其初始化为各数据类型的默认值.

2.2: 接着进行初始化, 在Java对象初始化过程中,主要涉及三种执行对象初始化的结构,分别是 实例变量初始化实例代码块初始化(static修饰的代码块,或者非static修饰的代码块) 以及 构造函数初始化。 (这三种初始化并不是按照顺序进行的).调用构造方法为成员变量赋值. 返回堆内存中对象的引用(相当于首地址)给引用变量student, 以后就可以通过student来引用堆内存中的对象了.

三、当创建多个不同的对象实例

一个类通过使用new运算符可以创建多个不同的对象实例, 这些对象实例将在堆中被分配不同的内存空间. 改变其中一个对象的状态不会影响其它对象的状态. 此时将在堆内存中分别为两个对象的成员变量name, age分配内存空间, 两个对象在堆内存中占据的空间互不相同. 如果相同, 则在堆内存中只创建了一个对象实例, 在栈中创建了两个对象的引用, 两个对象的引用同时指向一个对象实例.

四、String是一个特殊的包装类数据, 可以用一下两种方式创建

例题1: 

String b = "abc";
String c = "abc";

前提知识: JVM运行的时候,将内存分为两个部分,一部分是堆, 另一部分是栈. 堆中存放的是创建的对象, 而栈中存放的则是方法调用过程中局部变量或引用. 而设计Java字符串对象内存实现的时候, 在堆中又开辟了一块很小的内存, 其被称为字符串常量, 专门用来存放特定的字符串对象.

(1): 在栈中创建一个a的引用对象

(2): 查看字符串常量池中是否存在内容于"abc"相同的字符串对象.

(3): 若没有,则新创建一个包含改内容的字符串对象, 并让引用变量指向改对象. 例如创建字符串b的时候,字符串常量池中没有,则创建一个新对象,并让引用b指向改对象.

(4): 若已经存在包含改内容的字符串对象,则让字符串引用直接指向改对象. 例如,在创建字符串c的时候,字符串常量池中已经有包含改内容的对象了,所以引用c直接指向已有的对象.

注: 字符串引用b和c因为指向同一个对象,所以"=="测试成立,返回true;

 例题2: 

String a = "abc";
String b = new String ("abc");

(1): 在栈中创建a,b两个引用对象

(2): 然后在堆(不是常量池)中创建一个包含指定内容的字符串对象,并将字符串引用指向改对象.例如在上述代码中,使用new创建字符串b,其会直接在堆中创建一个内容为"abc"的字符串对象,并将引用b指向改对象.

jvm对new String中有一个优化的处理: 使用intern()方法,使用之后就会把b引用对象指向常量池中的字符串对象,而不是堆中的字符串对象,则a,b的地址都会是一样的

b = b.intern();
System.out.println(b==a)  //true

(3): 去字符串常量池中查看,是否有包含改内容的对象.

(4): 若有,则将new出来的字符串对象与字符串常量池中内容相同的对象联系起来.例如,本例中b所指向的对象与a中所指向的联系起来.

(5): 若没有,则在字符串常量池中再创建一个包含该内容的字符串对象,并将堆中的对象与字符串常量池中新创建出来的对象联系起来.

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值