小熊奶糖(BearCandy)
小熊奶糖(BearCandy)
发布于 2024-02-24 / 18 阅读
0
0

JavaScript (基本类型)primitive & (引用类型)reference Types,内存分配

JavaScript (基本类型)primitive & (引用类型)reference Types

由于储存方式不同把数据分成了两种类型基本类型(primitive)和引用类型(reference)

引用类型表示你操作的数据是同一个,也就是说当你传一个参数给另一个方法时,你在另一个方法中改变这个变量的值,那么调用这个方法是传入的变量的值也将改变。

值类型表示复制一个当前变量传给方法,当你在这个方法中改变这个变量的值时,最初生命的变量的值不会变。

基本[值]类型:数字,字符串,布尔值...

储存空间有限,储存复杂对象无效率。

  • 整型(4种):byte(1 byte)、short(2 byte)、int(4 byte)、long(8 byte)
  • 浮点型(2种):float(4 byte)、double(8 byte)
  • 字符型(1种):char(2 byte)
  • 逻辑型(1种):boolean

引用类型(初基本类型外的):对象(Object)

除了八种基本数据类型外,其他所有的类型都称为引用类型(数组、类、接口、字符串等)

对象通常有许多的属性或者方法,对象通常占的空间比较大,还可以动态调整属性,

let a =10;
let b =10;
let c = {
     name:"小白",
     age:25
};
let d = {
     name:"小白",
     age:25
};
if (a===b){
console.log("a=b");
};
if (c===d){
console.log("c=d");
};
//结果输出了a=b 没有输出c=d

判断的是栈内存是否相等,c和d的栈内存不一样所以结果为false

那如果赋值呢?

let a = 10;
let b = a;
if (a===b){
 console.log("a=b");
}
//结果输出了a=b

屏幕截图 2024-02-24 131325.png

在a=b后修改a的值

let a = 10;
let b = a;
a=20;
console.log(a);
console.log(b);
//结果输出了a=20 b=10

屏幕截图 2024-02-24 131622.png

这样如果对对象赋值呢?使得c=d

let c = {
     name:"小白",
     age:25
};
let d = c;
console.log(c);
console.log(d);
//结果都是{name:"小白",age:25}

屏幕截图 2024-02-24 131941.png

在内存中是这么储存的

那修改c中的name属性的值,d的值会跟着变化吗?

let c = {
     name:"小白",
     age:25
};
let d = c;
c.name = "小黑";
console.log(c);
console.log(d);
//结果都是{name:"小黑",age:25}

屏幕截图 2024-02-24 131941.png

答案是不会,他们用的一个地址,这个地址都指向同一个堆内存

内存分配

基本[值]类型

屏幕截图 2024-02-24 125259.png值类型(value type)的数据存放在栈内的一个变量中。即是在栈中分配内存空间,直接存储所包含的值,其值就代表数据本身。

但是也有特殊情况,如果某个类的实例有个值类型的字段,那么实际上该字段会和类实例保存在同一个地方,即堆中。不过引用类型的对象总是存储在堆中;如果一个结构的字段是引用类型,那么只有引用本身是和结构实例存储在一起的(在栈或堆上,视情况而定)。

值类型的数据具有较快的存取速度。

引用类型

屏幕截图 2024-02-24 125121.png

引用类型(reference type)的数据并不驻留在栈中,而是存储于堆中。即是在堆中分配内存空间,存储所包含的值,而栈中存储的是指向该值的地址。当访问一个具有引用类型的数据时,需要到栈中检查变量的内容,该变量引用堆中的一个实际数据。引用类型的数据比值类型的数据具有更大的存储规模和较低的访问速度。

注:

栈(stack)是一种后进先出的数据结构。在内存中,变量会被分配在栈上来进行操作。堆(heap)是用于为类型实例(对象)分配空间的内存区域,在堆上创建一个对象,会将对象的地址传给栈上的变量(反过来叫变量指向此对象,或者变量引用此对象)。

值类型总是分配在它声明的地方:作为字段时,跟随其所属的变量(实例)存储;作为局部变量时,存储在栈上。

值类型在内存管理方面具有更好的效率,并且不支持多态,适合用做存储数据的载体;

引用类型支持多态,适合用于定义 应用程序的行为。

回收机制

当一个堆内存中的对象没有被栈内存中表示地址的值“引用”时,这个对象就被称为垃圾对象,它无法被使用但却占据着内存中的区域,好比这样:

String s = new String("person");
s = new String("man");

s本来是指向堆内存中值为person的对象的,但是s突然讨厌person了,它指向了堆内存中的man对象了,person就像一个孤儿一样被s遗弃了,但是person比孤儿还要惨,因为没有什么能找的到它,除了位高权重的‘垃圾回收器’,不过被当官的找到往往没什么好事,尤其是这个‘垃圾回收器’,它会豪不留情把‘垃圾’们清理走,并且无情的销毁,以便释放内存。

Java中有垃圾回收机制,栈内存中的变量随着方法的结束内存自然销毁了,而用引用类型的时候,当方法结束的时候,这个对象可能被另一个引用类型所应用,不会销毁,只有当一个对象没有任何引用变量引用的时候,垃圾回收机制才会回收。

为什么Java中数据的存放有堆和栈之分?

答:当一个方法执行时,每个方法都会建立自己的内存栈,在这个方法内定义的变量将会逐个放入这块栈内存里,随着方法的执行结束,这个方法的内存栈也将自然销毁了。因此,所有在方法中定义的变量都是放在栈内存中的;当我们在程序中创建一个对象时,这个对象将被保存到运行时数据区中,以便反复利用(因为对象的创建成功通常较大),这个运行时数据区就是堆内存。堆内存中的对象不会随方法的结束而销毁,即使方法结束后,这个对象还可能被另一个引用变量所引用(方法的参数传递时很常见),则这个对象依然不会被销毁。只有当一个对象没有任何引用变量引用它时,系统的垃圾回收机制才会在合适的时候回收它。

装箱与拆箱

装箱:

装箱就是值类型到引用类型的转化过程。将一个值类型变量装箱成一个引用类型变量,首先会在托管堆上为新的引用类型变量分配内存空间,然后将值类型变量拷贝到托管堆上新分配的对象内存中,最后返回新分配的对象内存地址。

拆箱:

装箱操作是可逆的,所以还有拆箱操作。拆箱操作获取只想对象中包含值类型部分的指针,然后由程序员手动将其对应的值拷贝给值类型变量。


评论