java堆內存和棧內存的處理


前段時間學習二叉樹在處理刪除操作的時候遇到一個頭疼的問題:刪除節點的時候明明已經置null了可樹上該節點依舊存在,還必須執行node.father.left = null;才可以刪除node節點,尋找了一下原因發現還是因為對java內存管理理解不夠深入。

代碼如下:

	@Test
	public void testNode() {
		Node node1 = new Node("node1");
		Node node2 = new Node("node2");
		node2.father = node1;
		node1.next = node2;
		changeNode(node1.next);
		System.out.println(node1.next.name);
	}

	private void changeNode(Node node3) {
		node3 = null;
	}

  運行代碼之后發現本來已經在changeNode()中已經設置node3 = null;可依舊能輸出 node1.next.name。

要知為何?先理解幾個概念:

  1、棧內存存儲基本類型的變量對象的引用變量。
  2、堆內存用於存放由new創建的對象和數組。每new一個對象就在堆內存中開辟一個新的存儲空間存儲此實例對象。
  3、Person p = new Person();
    執行new命令時程序執行兩步:a:在堆內存中開辟一段空間,存儲new出來的對象;b:在棧內存中添加一個變量p,p中存放的是該對象在堆內存中開始存放處的物理地址。
  4、p = null;
    執行此步驟的時候程序只是更改棧內存中的P變量所保存的地址,把地址指向null,而並沒有操作堆內存(把p所指向的對象實例清空回收)。
  5、無論是形參或者實參,執行 XXX = null;操作時都是把XXX變量棧中存儲的地址改為指向null的地址。不操作堆中的數據。

下面就分析一下每步代碼在堆內存和棧內存中的變化:

  1、當new一個對象的時候,程序首先在堆內存中開辟一段空間實例化對象,同時在在棧內存中加入一個node1的變量,此變量中保存的是堆內存中物理地址的首地址。

  

2、當調用方法傳入參數的時候,在棧內存中添加一個局部變量node3,存儲node2的物理首地址。也即是把node2的值0X00011拷貝進node3的棧內存中。

     有網友說這是引用傳遞,其實傳遞的還是值,只不過node2的值本來就是物理地址,然后把這個物理地址值傳給node3.

3、當執行node3 = null; 的時候,程序會把node3中的0X00011變為一個指向null的地址 0Xaaaaa.但是程序不對堆內存中數據進行管理(堆內存中沒有引用指向的數據會在一定的時間通過GC進行處理)。

可以看到執行到此步只是把變量中的物理地址置空,但並沒有刪除node2在堆內存中的數據,這正是為什么刪除之后節點之后原數據依舊存在的原因。可以看到node1中的next依舊指向0X00011。

即使執行node2 = null,程序依舊不改變堆內存。

原數據依舊存在!

JVM內存處理實際情況要遠遠比這復雜得多,如果想深入了解可以找找 jvm內存處理機制相關資料,網上一搜一大堆,不再贅述。其實我也不知道。

  


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM