享元模式思想與實踐

1.享元模式定義

        通過減少創建對象的數量,以減少內存佔用和提高性能。這種類型的設計模式屬於結構型模式
        其實這句話包含兩個含義,一個是每創建一個對象,該對象都會佔用內存,與此同時對象的創建是需要消耗時間的。第二個是創建對象的數量是可以減少的,怎麼減少呢?通過複用已經創建好的對象,已經創建好的對象能夠替換本來打算創建的對象功能,怎麼複用呢,通過緩存,把已經創建好的對象放到緩存裏,下次需要的時候,直接從緩存裏拿,這樣不就節省了時間。
          享元模式與其說是一種設計模式,其實更多可以稱爲是一種計算機的設計思想,它的本質還是基於一個前提:計算機的內存的有限的,建立一些對象有時代價很大。這種設計思想早就在軟件領域遍地開花了。比如說數據庫連接池,即因建立數據庫連接代價較大,爲了降低數據庫操作的代價,通過建立數據庫連接池,提前建立多個數據庫連接放到“池子”裏,當應用邏輯需要操作數據庫時,直接從池子裏找一個空間的數據庫連接進行操作。

2.享元模式示例

      本文打算從java的字符串常量池來介紹“享元模式”這種思想。
        首先解釋一下jvm中容易混淆的三個概念:

  • class文件常量池:

        位於java類文件在編譯後會生成.class文件,這個.class文件中除了包含類的版本、字段、方法、接口等描述信息外,還有一項信息就是常量池(constant pool table),這是class文件常量池用於存放編譯器生成的各種字面量(Literal)和符號引用(Symbolic References)。 字面量就是我們所說的常量概念,如文本字符串、被聲明爲final的常量值等。 符號引用是一組符號來描述所引用的目標,符號可以是任何形式的字面量,只要使用時能無歧義地定位到目標即可(它與直接引用區分一下,直接引用一般是指向方法區的本地指針,相對偏移量或是一個能間接定位到目標的句柄)。
       

  • 運行時常量池:

       是class常量池被加載到內存之後的版本。(它同樣擁有所有的類)。不同之處是:它的字面量可以動態地添加(String#intern()),符號引用會被解析爲直接引用。

  • 字符串常量池

       是一個String Table類,實質上是一個Hash表,默認長度是1009。全局字符串池裏的內容是在類加載完成,經過驗證,準備階段之後在堆中生成字符串對象實例存到string pool中。以如下代碼爲例:

String s1= new String ("test abc"); 
String s2= "test abc";
String s3= "test "+"abc";

        當運行第一行代碼時,首先會創建一個字符串常量“test abc”,jvm會從字符串常量池中查看是否存在字符串常量”test abc”,在本例中是沒有的,因此,首先在堆中創建”test abc”這個對象,然後在字符串常量池中創建一個直接引用,指向剛纔創建的”test abc”對象(桔色文本框所示),接下來,會將”test abc”傳遞給String的構造函數(new String觸發),此時,會在堆中創建一個新的”test abc”,如圖中所示,最終棧中的變量s1指向了這個新的”test abc”對象。疑問來了,此時的字符串常量池中的引用以及桔色的”test abc”對象好像並沒有用,實際上,這裏是爲了以後的使用。
        當運行第二行代碼時,jvm再次發現要創建字符常量,此時jvm會從字符串常量池中查看是否已經有指向”test abc”的引用,發現已經有了,就把地址返回給變量s2,s2最終也引用了桔色的”test abc”。
        當運行第三行代碼時,實際上編譯器會做優化,將"test "+“abc"在編譯時,替換成"test abc”,因此實際上在運行時,第三行代碼與第二代碼完全一樣,同樣從字符串常量池中獲取了對象的引用,直接引用桔色的”test abc”,這也是享元模式思想的實踐運用。
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章