<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Java關(guān)鍵字new :對象的內(nèi)存分配原理

          共 8728字,需瀏覽 18分鐘

           ·

          2022-06-25 12:40

          一、關(guān)鍵字new概述

                  "new"可以說是Java開發(fā)者最常用的關(guān)鍵字,我們使用new創(chuàng)建對象,使用new并通過類加載器來實例化任何我們需要的東西,但你是否深入了解過new在編譯的瞬間都做了什么?

                  在Java中使用new關(guān)鍵字創(chuàng)建對象變得很容易了,事實上,對這些事情你是不需要考慮的。需要訪問一個文件嗎?只需要創(chuàng)建一個新的File實例:new File(“jdbc.properties”),對于大多數(shù)Java開發(fā)人員而言,這就是他們需要知道的一切,是不是很簡單呢?!但當(dāng)你使用了多個類加載器時,問題就不一樣了。


                  下面是對oracle官網(wǎng)文章的翻譯:

          http://docs.oracle.com/javase/tutorial/java/javaOO/objectcreation.html


                  我們都知道,一個類為對象提供了藍(lán)圖,你從一個類創(chuàng)建一個對象。以下語句從createobjectdemo程序創(chuàng)建一個對象并將其賦值給一個引用變量:

          Point originOne = new Point(23, 94); 
          Rectangle rectOne = new Rectangle(originOne, 100, 200);
          Rectangle rectTwo = new Rectangle(50, 100);


          第一行創(chuàng)建了一個 Point 類的對象,第二個和第三個線創(chuàng)建一個Rectangle 矩形類的對象。

          這些陳述中的每一個都有三個部分(詳細(xì)討論):

          聲明Declaration:粗體代碼是將變量名稱與對象類型關(guān)聯(lián)的變量聲明。

          實例化Instantiating :new關(guān)鍵字是一個java運算符,它用來創(chuàng)建對象。

          初始化Initialization:new運算符,隨后調(diào)用構(gòu)造函數(shù),初始化新創(chuàng)建的對象。

          聲明一個變量來指向一個對象,即引用

                  在此之前,你知道,要聲明一個變量,你需要寫:type name;

                  這將告訴編譯器你將使用name引用一個type類型的對象。用一個原始變量,這個聲明也保留了適當(dāng)?shù)膬?nèi)存量的變量。

          你也可以在自己的行上聲明一個引用變量。例如:Point  originone;

                 如果你只是聲明一個像originone這樣的引用變量,其價值將待定,直到有一個對象真正被創(chuàng)造和分配給它。只是簡單地聲明一個引用變量而并沒有創(chuàng)建一個對象。對于這樣,你需要使用new運算符。在你的代碼中使用它之前,你必須指定一個對象給originone。否則,你會得到一個編譯器錯誤-----空指針異常。

                  處于這種狀態(tài)的變量,目前沒有引用任何的對象,可以說明如下(變量名,originone,一個引用沒指向任何對象)。

          實例化一個類對象

                  new運算符實例化一個類對象,通過給這個對象分配內(nèi)存并返回一個指向該內(nèi)存的引用。new運算符也調(diào)用了對象的構(gòu)造函數(shù)。

                  注意:“實例化一個類的對象”的意思就是“創(chuàng)建對象”。創(chuàng)建對象時,你正在創(chuàng)造一個類的“實例”,因而“實例化”一個類的對象。

                  new運算符需要一個單一的,后綴參數(shù),需要調(diào)用構(gòu)造函數(shù)。構(gòu)造函數(shù)的名稱提供了需要實例化類的名稱。

                  new運算符返回它所創(chuàng)建的對象的引用。此引用通常被分配給一個合適的類型的變量,如:

          Point  originone =new Point(2394);


                  由new運算符返回的引用可以不需要被賦值給變量。它也可以直接使用在一個表達(dá)式中。例如:  

          int height = new Rectangle().height;


          初始化一個類對象

          這是Point類的代碼

          public class Point {    
             public int x = 0;    
             public int y = 0;    
             //constructor
             public Point(int a, int b) {
                 x = a;
                 y = b;
             }
          }


              這個類包含一個單一的構(gòu)造函數(shù)。你可以識別一個構(gòu)造函數(shù),因為它的聲明使用與類具有相同的名稱,它沒有返回類型。在Point類構(gòu)造函數(shù)的參數(shù)是兩個整數(shù)參數(shù),如代碼聲明(int  a,int  b)。下面的語句提供了94和23作為這些參數(shù)的值:

          Point originOne = new Point(23, 94);    //結(jié)果可描述為下圖

          這是Rectangle類,包含4個版本的構(gòu)造方法

          public class Rectangle {    
             public int width = 0;    
             public int height = 0;    
             public Point origin;    // four constructors
             public Rectangle() {
                 origin = new Point(0, 0);
             }    
             public Rectangle(Point p) {
                 origin = p;
             }    
             public Rectangle(int w, int h) {
                 origin = new Point(0, 0);
                 width = w;
                 height = h;
             }    
             public Rectangle(Point p, int w, int h) {
                 origin = p;
                 width = w;
                 height = h;
             }    
             // a method for moving the rectangle
             public void move(int x, int y) {
                 origin.x = x;
                 origin.y = y;
             }    
             // a method for computing the area of the rectangle
             public int getArea() {        
                 return width * height;
             }
          }
          public class Rectangle {    
             public int width = 0;    
             public int height = 0;    
             public Point origin;    
             // four constructors
             public Rectangle() {
                 origin = new Point(0, 0);
             }    
             public Rectangle(Point p) {
                 origin = p;
             }    
             public Rectangle(int w, int h) {
                 origin = new Point(0, 0);
                 width = w;
                 height = h;
             }    
             public Rectangle(Point p, int w, int h) {
                 origin = p;
                 width = w;
                 height = h;
             }    
             // a method for moving the rectangle
             public void move(int x, int y) {
                 origin.x = x;
                 origin.y = y;
             }    // a method for computing the area of the rectangle
             public int getArea() {        
                 return width * height;
             }
          }



                  每個構(gòu)造函數(shù)都允許你為矩形的起始值、寬度和高度提供初始值,同時使用原始類型和引用類型。如果一個類有多個構(gòu)造函數(shù),它們必須有不同的簽名。java編譯器區(qū)分構(gòu)造函數(shù)基于參數(shù)的數(shù)量和類型。當(dāng)java編譯器遇到下面的代碼,它知道在矩形類,需要一點爭論,后面跟著兩個整數(shù)參數(shù)調(diào)用構(gòu)造函數(shù):   

          Rectangle rectOne = new Rectangle(originOne, 100, 200);


          結(jié)果可描述為下圖:

          總結(jié):

          1.Java關(guān)鍵字new是一個運算符。與+、-、*、/等運算符具有相同或類似的優(yōu)先級。

          2.創(chuàng)建一個Java對象需要三部:聲明引用變量、實例化、初始化對象實例。

          3.實例化:就是“創(chuàng)建一個Java對象”-----分配內(nèi)存并返回指向該內(nèi)存的引用。

          4.初始化:就是調(diào)用構(gòu)造方法,對類的實例數(shù)據(jù)賦初值。

          5.Java對象內(nèi)存布局:包括對象頭和實例數(shù)據(jù)。如下圖:


                  對象頭:它主要包括對象自身的運行行元數(shù)據(jù),比如哈希碼、GC分代年齡、鎖狀態(tài)標(biāo)志等;同時還包含一個類型指針,指向類元數(shù)據(jù),表明該對象所屬的類型。

                  實例數(shù)據(jù):它是對象真正存儲的有效信息,包括程序代碼中定義的各種類型的字段(包括從父類繼承下來的和本身擁有的字段)。

                  在hotSpot虛擬機中,對象在內(nèi)存中的布局可以分成對象頭、實例數(shù)據(jù)、對齊填充三部分。對齊填充:它不是必要存在的,僅僅起著占位符的作用。

          6.Object obj = new Object();

                  那“Object obj”這部分的語義將會反映到Java棧的本地變量表中,作為一個reference類型數(shù)據(jù)出現(xiàn)。而“new Object()”這部分的語義將會反映到Java堆中,形成一塊存儲了Object類型所有實例數(shù)據(jù)值(Instance Data,對象中各個實例字段的數(shù)據(jù))的結(jié)構(gòu)化內(nèi)存,根據(jù)具體類型以及虛擬機實現(xiàn)的對象內(nèi)存布局(Object Memory Layout)的不同,這塊內(nèi)存的長度是不固定的。另外,在Java堆中還必須包含能查找到此對象類型數(shù)據(jù)(如對象類型、父類、實現(xiàn)的接口、方法等)的地址信息,這些類型數(shù)據(jù)則存儲在方法區(qū)中。

          二、內(nèi)存分配原理

                  內(nèi)存分配,在哪分配?-------盡管Java對象的內(nèi)存分配可以使用逃逸分析技術(shù)和棧外分配,但不可否認(rèn)這僅僅是為了降低GC回收頻率以及提升GC回收效率的一種輔助手段,所以Java堆區(qū)仍然是分配/存儲對象實例的主要區(qū)域,這一點毋庸置疑。


                  參考《Java虛擬機規(guī)范(第7版)》的描述,JVM包含三種引用類型,分別是類型 (class type),數(shù)組類型(array type)和接口類型(interface type),這些引用類型的值則分別 由類實例、數(shù)組實例以及實現(xiàn)了某個接口的派生類實例負(fù)責(zé)動態(tài)創(chuàng)建,那么JVM中究 竟是如何為這些類型創(chuàng)建對應(yīng)的對象實例呢?-------------如果是在Java語法層面上創(chuàng)建 一個對象,無非就是使用一個簡單的new關(guān)鍵字即可,但是在JVM中就沒有那么簡 單了,其實牽扯到細(xì)節(jié)的實現(xiàn)相當(dāng)復(fù)雜,而且過程繁多。簡單地說,當(dāng)Java語法層面 使用new關(guān)鍵字創(chuàng)建一個Java對象時,JVM首先會檢查這個new指令的參數(shù)能否在常 量池中定位到一個類的符號引用,然后檢查與這個符號引用相對應(yīng)的類是否已經(jīng)成功經(jīng) 歷加載、解析和初始化等步驟,當(dāng)類完成裝載步驟之后,就已經(jīng)完全確定出創(chuàng)建對象實 例時所需的內(nèi)存空間大小,接下來JVM將會對其進(jìn)行內(nèi)存分配,以存儲所生成的對象 實例。如下圖所示:


                  

            為新對象分配內(nèi)存是一件非常嚴(yán)謹(jǐn)和復(fù)雜的任務(wù),JVM的設(shè)計者們不僅需要考慮內(nèi)存如何分配、在哪分配等問題,并且由于內(nèi)存分配算法與內(nèi)存回收算法密切相關(guān),所以還要考慮GC執(zhí)行完內(nèi)存回收后是否會在內(nèi)存空間中產(chǎn)生內(nèi)部碎片。如果內(nèi)存空間以規(guī)整和有序的的方式分布,當(dāng)為新對象分配內(nèi)存時,只需要修改指針的偏移量將新對象分配在第一個空閑內(nèi)存位置上,這種分配方式就叫做指針碰撞(Bump the Pointer),反之則只能使用空閑列表(Free List)執(zhí)行內(nèi)存分配。


                基于分代的概念,Java堆區(qū)如果進(jìn)一步細(xì)分的話,還可分為:新生代 ( Young )和老年代 ( Old );這也就是JVM采用的“分代思想”,簡單說,就是針對不同特征的java對象采用不同的策略實施存放和回收,所用分配機制和回收算法就不一樣。新生代 ( Young ) 又被劃分為三個區(qū)域:Eden、From Survivor、To Survivor。(《Java虛擬機精講》) 

                 分代收集算法:采用不同算法處理[存放和回收]Java瞬時對象和長久對象。大部分Java對象都是瞬時對象,朝生夕滅,存活很短暫,通常存放在Young新生代,采用復(fù)制算法對新生代進(jìn)行垃圾回收。老年代對象的生命周期一般都比較長,極端情況下會和JVM生命周期保持一致;通常采用標(biāo)記-壓縮算法對老年代進(jìn)行垃圾回收。


                  這樣劃分的目的是為了使 JVM 能夠更好的管理堆內(nèi)存中的對象,包括內(nèi)存的分配以及回收。那么Java堆區(qū)被細(xì)分成這么多區(qū)域,對象實例究竟是存儲在堆區(qū)中的那一個區(qū)域下呢?在JVM運行數(shù)據(jù)區(qū)中,堆區(qū)和方法區(qū)是線程共享的數(shù)據(jù)區(qū),任何線程都可以訪問到這兩個區(qū)域中的共享數(shù)據(jù),由于對象實例的創(chuàng)建在JVM中非常頻繁,因此在并發(fā)環(huán)境下從堆中劃分內(nèi)存空間是非線程安全的,所以務(wù)必需要保證數(shù)據(jù)操作的原子性。基于線程安全的考慮,如果一個類在分配內(nèi)存之前成功完成的類加載,JVM會優(yōu)先選擇在TLAB(Thread Local Allocation Buffer,本地線程分配緩存區(qū))中為對象實例分配內(nèi)存空間,TLAB在Java堆中是一塊線程私有數(shù)據(jù)區(qū),它包含在Eden空間內(nèi),除了可以避免一系列的非線程安全問題外,同時還能提高內(nèi)存分配的吞吐量,因此我們可以將這種內(nèi)存分配方式稱之為快速分配策略。

                  當(dāng)為對象成功分配好所需的內(nèi)存空間(實例化)后,JVM接下來要做的任務(wù)就是-------初始化對象實例。JVM首先會對分配好的內(nèi)存空間進(jìn)行零值初始化,這一步操作確保了對象的實例字段在Java代碼中可以不用賦初值就能夠直接使用,程序能夠訪問到這些字段的數(shù)據(jù)類型所對應(yīng)的零值。


                  對分配后的內(nèi)存空間進(jìn)行零值初始化后,JVM就會初始化對象頭和實例數(shù)據(jù)。最后將對象引入棧后,再更新PC寄存器中的字節(jié)碼指令地址。經(jīng)過這一系列的操作步驟之后每一個Java對象實例才算是真正的創(chuàng)建成功。


          總結(jié):

          1.在Java語法層面上創(chuàng)建一個對象,使用一個簡單的new關(guān)鍵字即可,但是在JVM中細(xì)節(jié)的實現(xiàn)相當(dāng)復(fù)雜,而且過程繁多。

          2.當(dāng)Java語法層面使用new關(guān)鍵字創(chuàng)建一個Java對象時,JVM首先會檢查相對應(yīng)的類是否已經(jīng)成功經(jīng)歷加載、解析和初始化等步驟;當(dāng)類完成裝載步驟之后,就已經(jīng)完全確定出創(chuàng)建對象實例時所需的內(nèi)存空間大小,才能對其進(jìn)行內(nèi)存分配,以存儲所生成的對象實例。

          3.實例化之后,進(jìn)行初始化(初始化對象頭和實例數(shù)據(jù))。

          4.內(nèi)存分配方式有:指針碰撞(Bump the Pointer)、快速分配策略、空閑列表(Free List)。

          5.在并發(fā)環(huán)境下從堆中劃分內(nèi)存空間是非線程安全的,new運算符具有-------數(shù)據(jù)操作的原子性;也就是說創(chuàng)建一個Java對象分配內(nèi)存,要么所有步驟都成功,返回對象的引用,要么回歸到創(chuàng)建之前的內(nèi)存狀態(tài),返回為NULL。

          6.通過new創(chuàng)建一個Java對象,如果成功則返回這個對象的引用,開發(fā)者不可直接操作對象實例,需要通過這個引用“牽引”。

                看完這篇文章,相信你對Java關(guān)鍵字new及Java對象的完整創(chuàng)建過程有了更深的認(rèn)識,就不會只停留在new一個對象就完了。

          原文地址:

          https://blog.csdn.net/ljheee/article/details/52235915


          如有文章對你有幫助,

          在看”和轉(zhuǎn)發(fā)是對我最大的支持!

          瀏覽 64
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  91免费国产 | 青娱乐操逼网站 | 91九色porn蝌蚪 | 欧美性爱黄色 | 青操青青操逼网 |