project中的堆疊記憶體,記憶體地址引用,gc相關問題
專案中遇到了關於js記憶體,引用型別,gc機制相關的問題,記錄下來。
首先復現一下程式碼:
const option = {
yAxis: [{
type: 'value',
......
}]
};
option.yAxis.push(option.yAxis[0]);
option.yAxis[0].min = 1;
option.yAxis[1].min = 2;
發現option.yAxis下面的min的值都是2;第一反應是以為之前的程式碼寫的有問題,所以打斷點,打日誌,發現都正常,然後想到了有可能是記憶體指標導致的,然後可以做以下兩種修改。
const option = {
yAxis: [{
type: 'value',
......
}]
};
option.yAxis.push(JSON.parse(JSON.stringify(option.yAxis[0])));
option.yAxis[0].min = 1;
option.yAxis[1].min = 2;
或者
const option = {
yAxis: [{
type: 'value',
......
}]
};
option.yAxis.push(option.yAxis[0]);
option.yAxis[0] = {min: 1};
option.yAxis[1] = {min: 2};
以上兩種寫法都可以規避記憶體指標引用導致的資料問題。
剖析以下問題的本質吧:
- JS基本資料型別:Null,Undefined,Number,Boolean,Symbol,String
- JS複雜資料型別:Object Array (也算是object)
當我們在程式碼裡面:
var num = 123;
var obj = { name: 'obj' };
var obj_copy = obj;
此時會開闢兩塊記憶體空間,一個儲存123,一個儲存 { name: 'obj' }; 其中是num的指標會直接指向棧記憶體中的123,obj的指標會指向堆記憶體中的 { name: 'obj' },obj_copy的引用指標會存放在棧記憶體中;
如果我們編輯以下程式碼:
var num = 123;
var obj = { name: 'obj' };
var obj_copy = obj;
obj.name = 'xxx';
那麼我們會發現obj_copy的name也會變成xxx,其實就是因為產生了指標以引用,指向的是同一塊記憶體空間;
如果編輯以下程式碼:
var num = 123;
var obj = { name: 'obj' };
var obj_copy = obj;
obj = {name: 'xxx'};
那麼我們會發現obj_copy的name還是obj,這是因為obj = {name: 'xxx'};會產生一塊新的記憶體空間,然後obj會產生一次引用,obj_copy的引用跟該引用沒有任何毛線關係。那麼我們專案中的問題就迎刃而解了。
寫到這裡,突然想到了es6 的const 變數申明:
const object = { name: 'object'};
const num = 5;
object.name = 'new_object';
num = 4;
我們會發現object的name確實變成了字串new_object,但是num=4會報錯,為什麼呢?其實這個問題跟堆疊記憶體沒有關係,還是跟引用資料型別有關;
看下面的程式碼:
let numA = 1;
let numB = numA;
numA = 2;
console.log(numB );
我們發現numB的值仍然是1,其實基本資料型別也是存在引用的,只是基本資料型別無法像object一樣去更改某個key的值而已,就比如一座房子和一把板凳,如果你改變了房子或者凳子,那麼他就是實實在在地改變了,如果你只是改變了一座房子內部的一部分,它仍然是房子。
說到這裡,不得不提一下es6的 WeekMap了(也是借用了阮大的例子吧,哈哈哈!)
const wm = new WeakMap();
const element = document.getElementById('example');
wm.set(element, 'some information');
wm.get(element) // "some information"
WeekMap 是一種弱引用,也就是說,該節點的引用計數是1,如果element設定為null,Weakmap 儲存的這個鍵值對,也會自動消失。
說到這裡,就要說一下javascript的垃圾回收機制了,分為兩種型別吧,一種是標記清除 ,一種是引用計數。
let fn = function (){
let a = 1;
return a;
}
fn();
在執行fn函式時,變數a被標記為進入環境,在函式沒有被執行結束之前,是不能釋放該變數所指向的記憶體的,當函式執行完之後,變數會被標記為離開環境,則會被gc回收
let fn = function (){
let a = 1;
return a;
}
let back = fn();
如果程式碼寫成這樣的話,變數a所佔用的記憶體是不會被gc的,因為在外部存在了引用,雖然a已經離開了fn的執行環境,但是a的引用計數是2,所以不會被gc回收清除。
function problem(){
var objectA = new Object();
var objectB = new Object();
objectA.someOtherObject = objectB;
objectB.anotherObject = objectA;
}
上述情況也是objectA 和B都離開了函式環境,但是因為存在迴圈引用,所以引用計數都不為0,所以記憶體就不會得到回收,在個別情況下,需要手動回收。
另外,gc時,會阻塞主執行緒,所以平常寫程式碼的時候一定要注意相關問題,務必規避記憶體洩漏的相關問題。
相關文章
- JS中堆疊記憶體的練習JS記憶體
- JS中的棧記憶體、堆記憶體JS記憶體
- 堆疊和記憶體的關係 細說記憶體
- jvm堆記憶體和GC簡介JVM記憶體GC
- javascript堆疊記憶體分配的區別JavaScript記憶體
- JVM堆外記憶體問題排查JVM記憶體
- windows核心程式設計--記憶體堆疊Windows程式設計記憶體
- 直接記憶體和堆記憶體誰快記憶體
- [20191220]關於共享記憶體段相關問題.txt記憶體
- jvm 堆記憶體JVM記憶體
- Java堆記憶體Heap與非堆記憶體Non-HeapJava記憶體
- [轉載] Java直接記憶體與堆記憶體Java記憶體
- Java堆疊的深度分析及記憶體管理技巧Java記憶體
- JVM記憶體-GC策略JVM記憶體GC
- 記憶體優化相關記憶體優化
- 小計:引用型別記憶體分配問題型別記憶體
- JavaScript變數,資料和記憶體的相關問題JavaScript變數記憶體
- iOS 記憶體管理相關面試題iOS記憶體面試題
- NameNode堆記憶體估算記憶體
- JVM中記憶體和GC的介紹JVM記憶體GC
- 分析ThreadLocal的弱引用與記憶體洩漏問題thread記憶體
- golang的記憶體相關內容Golang記憶體
- [20191223]關於共享記憶體段相關問題3.txt記憶體
- JVM記憶體GC的騙局JVM記憶體GC
- jvm:記憶體模型、記憶體分配及GC垃圾回收機制JVM記憶體模型GC
- 深入理解Java的堆記憶體和執行緒記憶體Java記憶體執行緒
- 關於PHP記憶體洩漏的問題PHP記憶體
- java 堆外記憶體排查Java記憶體
- JVM堆記憶體詳解JVM記憶體
- 排查Java的記憶體問題Java記憶體
- 探究 iOS 記憶體問題iOS記憶體
- SQLServer記憶體問題分析SQLServer記憶體
- Go記憶體分配和GC的理解Go記憶體GC
- windows memeory 記憶體相關術語Windows記憶體
- JVM GC 與 記憶體分配策略JVMGC記憶體
- 堆外記憶體及其在 RxCache 中的使用記憶體
- Chrome 再次最佳化記憶體佔用問題,新增記憶體釋放開關Chrome記憶體
- 關於JVM堆外記憶體的一切JVM記憶體