最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java中的引用和動態(tài)代理的實現(xiàn)詳解

 更新時間:2017年11月21日 17:22:28   作者:TheAlchemist  
這篇文章主要介紹了Java中的引用和動態(tài)代理的實現(xiàn)詳解,涉及Java中的引用類型,JVMGC的可達性分析,代理模式等相關(guān)內(nèi)容,具有一定參考價值,需要的朋友可以了解下。

我們知道,動態(tài)代理(這里指JDK的動態(tài)代理)與靜態(tài)代理的區(qū)別在于,其真實的代理類是動態(tài)生成的。但具體是怎么生成,生成的代理類包含了哪些內(nèi)容,以什么形式存在,它為什么一定要以接口為基礎(chǔ)?

如果去看動態(tài)代理的源代碼(java.lang.reflect.Proxy),會發(fā)現(xiàn)其原理很簡單(真正二進制類文件的生成是在本地方法中完成,源代碼中沒有),但其中用到了一個緩沖類java.lang.reflect.WeakCache<ClassLoader,Class<?>[],Class<?>>,這個類用到了弱引用來構(gòu)建。

在JDK的3個特殊引用中,弱引用是使用范圍最廣的,它的特性也最清晰,相對而言,其他兩種邏輯稍顯晦澀,源碼中的注釋也語焉不詳。本文將簡單介紹幾種引用的行為特征,然后分析一下弱引用的一些實際應用場景,其中包含了動態(tài)代理中的實現(xiàn)。本文將包含以下內(nèi)容:

JDK中的引用類型

不同引用類型對GC行為的影響

引用類型的實現(xiàn)

ThreadLocal對弱引用的使用

動態(tài)代理對弱引用的實現(xiàn)

虛引用如何導致內(nèi)存泄漏

JDK中「引用(Reference)」的類型

Java的所有運行邏輯都是基于引用的,其形態(tài)類似于不可變的指針,所以在Java中會有一些很繞的概念,比如說,Java中函數(shù)的傳參是值傳遞,但這里所說的值,其實是引用的值,所以你可以通過一個函數(shù)的參數(shù)來修改其對象的值。另一方面,Java中還存在一些基本數(shù)據(jù)類型,它們沒有引用,傳遞的是真實的值,所以不能在函數(shù)內(nèi)部修改參數(shù)的值。

關(guān)于引用,Java中有這樣幾種:

1.強引用

所有對象的引用默認為強引用,普通代碼中,賦值語句之間傳遞的都是強引用,如果一個對象可以被某個線程(活著的,下同)通過強引用訪問到,則稱之為強可達的(StronglyReachable)。

強可達的對象不會被GC回收。

2.軟引用(SoftReference<T>)

當一個對象不是強可達的,但可以被某個線程通過軟引用訪問到,則稱之為軟可達的(SoftlyReachable)。

軟可達的對象的引用只有在內(nèi)存不足時會被清除,使之可以被GC回收。在這一點上,JVM是不保證具體什么時候清除軟引用,但可以保證在OOM之前會清除軟可達的對象。同時,JVM也不保證軟可達的對象的回收順序,但OracleJDK的文檔中提到,最近創(chuàng)建和最近使用的軟可達對象往往會最后被回收,與LRU類似。

關(guān)于軟可達的對象何時被回收,可以參考Oracle的文檔。

3.弱引用(WeakReference<T>)

當一個對象不是強可達的,也不是軟可達的,但可以被某個線程通過弱引用訪問到,則稱之為弱可達的(WeaklyReachable)。

弱引用是除了強引用之外,使用最廣泛的引用類型,它的特性也更簡單,當一個對象是弱可達時,JVM就會清除這個對象上的弱引用,隨后對這個對象進行回收動作。

4.虛引用(PhantomReference<T>)

當一個對象,通過以上幾種可達性分析都不可達,且已經(jīng)finalized,但有虛引用指向它,則它是虛可達的(PhantomReachable)。

虛引用是行為最詭異,使用方法最難的引用,后邊會講到。

虛引用不會影響GC,它的get()方法永遠返回null,唯一的用處就是在GCfinalize一個對象之后,它會被放到指定的隊列中去。關(guān)于這個隊列會在下邊說明。

不同GC行為背后的原理

JVMGC的可達性分析

JVM的GC通過可達性分析來判斷一個對象是否可被回收,其基本思路就是以GCRoots為起點遍歷鏈路上所有對象,當一個對象和GCRoots之間沒有任何的直接或間接引用相連時,就稱之為不可達對象,則證明此對象是不可用的。

而進一步,Java中又定義了如上所述的4種不同的可達性,用來實現(xiàn)更精細的GC策略。

Finalaze和ReferenceQueue

對于普通的強引用對象,如果其變成不可達之后,通常GC會進行Finalize(Finalize主要目的是讓用戶可以自定義釋放資源的過程,通常是釋放本地方法中使用的資源),然后將它的對象銷毀回收,但對于本文中討論的3種引用,還有可能在這個過程中做一些別的事情:

GC根據(jù)約定的規(guī)則來決定是否清除這些引用

這方面上一節(jié)已經(jīng)講過了,每個引用類型都有約定的處理規(guī)則。

如果它們注冊了引用隊列,在Finalize對象后,將引用的對象放入隊列。

主要用來使開發(fā)者可以得到對象被銷毀的通知,當然,如虛引用這樣的,其引用不會自動被清除,所以它可以阻止其所引用的對象被回收。

引用(java.lang.ref.Reference<T>)對象的狀態(tài)

這里所說的「引用對象」指的是由類java.lang.ref.Reference<T>生產(chǎn)的對象,這個對象上保持了「需要特殊處理的」對「目標對象」的引用。

引用對象有4種狀態(tài),根據(jù)它與其注冊的隊列的關(guān)系,分為以下4種:

Active

引用對象的初始狀態(tài),表示GC要按照特殊的邏輯來處理這個對象,大致方法就是按照上一節(jié)提到的。

Pending

如果一個引用對象,其注冊了隊列,在入隊之前,會進入這個狀態(tài)。

Enqueued

一個引用對象入隊后,進入這個狀態(tài)。

Inactive

一個引用對象出隊后,或者沒有注冊隊列,其隊列是一個特殊的對象java.lang.ref.ReferenceQueue.NULL,表示這個對象已經(jīng)沒有用了。

幾種引用的實際應用

日常開發(fā)工作中,用到除強引用之外的引用的可能性很小,只有在處理一些性能敏感的邏輯時,才需要考慮使用這些特殊的引用,下面就舉幾個相關(guān)的實際例子,分析其使用場景。

軟引用

弱引用的使用比較簡單,如Guava中的LocalCache中就是用了SoftReference來做緩存。

弱引用

弱引用是使用的比較多的,從上文的描述可知:對于一個「目標對象A」,如果還有強引用指向它,那么從一個弱引用就可以訪問到A,一旦沒有強引用指向它,那么就可以認為,從這個弱引用就訪問不到A了(實際情況可能會有偏差)。

根據(jù)這個特點,JDK中注釋說到,弱引用通常用來做映射表(canonicalizingmapping),總結(jié)下來映射表有這樣2個特點:

如果表中的Key(或者Value)還存在強引用,則可以通過Key訪問到Value,反之則訪問不到

換句話說,只要有原始的Key,就能訪問到Value。

映射表本身不會影響其中Key或者Value的GC

在JDK中有很多個地方使用了它的這個特點,下面是2個具有代表性的實例。

1.ThreadLocal

ThreadLocal的原理比較簡單,線程中保持了一個以ThreadLocal為Key的ThreadLocal.ThreadLocalMap對象threadLocals,其中的Entry如代碼1中所示:

//代碼1
static class Entry extends WeakReference<ThreadLocal<?>> {
  /** The value associated with this ThreadLocal. */
  Object value;
  //其保持了對作為Key的ThreadLocal對象的弱引用
  Entry(ThreadLocal<?> k, Object v) {
    super(k);
    value = v;
  }
}

其引用關(guān)系如下圖所示:

ThreadLocal中的引用關(guān)系

從上圖可以看出,當引用2被清除之后(ThreadLocal對象不再使用),如果引用4為強引用,則不論引用1是否還存在,只要Thread對象還沒死,則對象1和對象2永遠不會被釋放。

2.動態(tài)代理

動態(tài)代理是Java世界一個十分重要的特性,對于需要做AOP的業(yè)務邏輯十分重要。JDK本身提供了基于反射的動態(tài)代理機制,其原理大致是要通過預先定義的接口(interface)來動態(tài)的生成代理類,并將之代理到InvocationHandler的實例上去。JDK的動態(tài)代理使用起來很簡單,如下代碼2中所示:

//代碼2
package me.lk;

import java.lang.reflect.*;

public class TestProxy {
  /**
   * 兩個預定義的需要被代理的接口
   */
  public static interface ProxiedInterface {

    void proxiedMethod();
  }
  public static interface ProxiedInterface2 {

    void proxiedMethod2();
  }

  /**
   * 真正的處理邏輯
   */
  public static class InvoHandler implements InvocationHandler {

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      System.out.println("in proxy:" + method.getName());
      //其他邏輯
      System.out.println("in proxy end");
      return null;
    }
    
  }
  public static void main(String[] args) {
    InvoHandler ih = new InvoHandler();
    ProxiedInterface proxy = (ProxiedInterface) Proxy.newProxyInstance(TestProxy.class.getClassLoader(), new Class[]{ProxiedInterface.class, ProxiedInterface2.class}, ih);
    proxy.proxiedMethod();
    ProxiedInterface2 p = (ProxiedInterface2) proxy;
    p.proxiedMethod2();
  }

}

動態(tài)代理的實現(xiàn)原理

關(guān)于Java中的動態(tài)代理,我們首先需要了解的是一種常用的設(shè)計模式--代理模式,而對于代理,根據(jù)創(chuàng)建代理類的時間點,又可以分為靜態(tài)代理和動態(tài)代理。

代理模式是常用的java設(shè)計模式,他的特征是代理類與委托類有同樣的接口,代理類主要負責為委托類預處理消息、過濾消息、把消息轉(zhuǎn)發(fā)給委托類,以及事后處理消息等。代理類與委托類之間通常會存在關(guān)聯(lián)關(guān)系,一個代理類的對象與一個委托類的對象關(guān)聯(lián),代理類的對象本身并不真正實現(xiàn)服務,而是通過調(diào)用委托類的對象的相關(guān)方法,來提供特定的服務。簡單的說就是,我們在訪問實際對象時,是通過代理對象來訪問的,代理模式就是在訪問實際對象時引入一定程度的間接性,因為這種間接性,可以附加多種用途。

參閱:Java設(shè)計模式之代理模式原理及實現(xiàn)代碼分享

其實現(xiàn)原理其實也很簡單,就是在方法Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)中動態(tài)生成一個「實現(xiàn)了interfaces中所有接口」并「繼承于Proxy」的代理類,并生成相應的對象。

//代碼3
public static Object newProxyInstance(ClassLoader loader,
                     Class<?>[] interfaces,
                     InvocationHandler h)
    throws IllegalArgumentException
  {
	Objects.requireNonNull(h);
	final Class<?>[] intfs = interfaces.clone();
	//驗證真實調(diào)用者的權(quán)限
	final SecurityManager sm = System.getSecurityManager();
	if (sm != null) {
		checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
	}
	//查詢或生成代理類
	Class<?> cl = getProxyClass0(loader, intfs);
	//驗證調(diào)用者對代理類的權(quán)限,并生成對象
	。。。省略代碼
}
private static Class<?> getProxyClass0(ClassLoader loader,
                    Class<?>... interfaces) {
	if (interfaces.length > 65535) {
		throw new IllegalArgumentException("interface limit exceeded");
	}
	// 通過緩存獲取代理類
	return proxyClassCache.get(loader, interfaces);
}

生成動態(tài)類的邏輯在方法java.lang.reflect.Proxy.ProxyClassFactory.apply(ClassLoader, Class<?>[]),代碼如下:

//代碼4
@Override
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {

  Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
  //驗證接口,驗證接口是否重復,驗證loader對接口的可見性
  
  //生成包名和修飾符

  //生成類
  byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
    proxyName, interfaces, accessFlags);
  try {
    return defineClass0(loader, proxyName,
              proxyClassFile, 0, proxyClassFile.length);
  } catch (ClassFormatError e) {
    /*
     * 生成失敗
     */
    throw new IllegalArgumentException(e.toString());
  }
}

動態(tài)代理中的緩存策略

為了更高效的使用動態(tài)代理,Proxy類中采用了緩存策略(代碼3中的proxyClassCache )來緩存動態(tài)生成的代理類,由于這個緩存對象是靜態(tài)的,也就是說一旦Proxy類被加載,proxyClassCache 很可能永遠不會被GC回收,然而它必須要保持對其中的ClassLoader和Class的引用,如果這里使用強引用,則它們也隨著proxyClassCache 永遠不會被GC回收。

不再使用的類和類加載器如果無法被GC,其內(nèi)存泄漏的風險很大。所以WeakCache中設(shè)計為,「傳入的類加載器」和「生成的代理類」為弱引用。

類和類加載器是相互引用的,而類加載器的內(nèi)存泄漏可能會帶來很嚴重的問題,有興趣可以去看這篇文章:Reloading Java Classes 201: How do ClassLoader leaks happen?

//代碼5
/**
 * a cache of proxy classes
 */
//ClassLoader  用來加載預定義接口(interface)和生成代理類的類加載器
//Class<?>[]   預定義接口(interface)
//Class<?>    生成的代理類
private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
  proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

/**
 * CacheKey containing a weakly referenced {@code key}. It registers
 * itself with the {@code refQueue} so that it can be used to expunge
 * the entry when the {@link WeakReference} is cleared.
 */
private static final class CacheKey<K> extends WeakReference<K> 

/**
 * A {@link Value} that weakly references the referent.
 */
private static final class CacheValue<V>
  extends WeakReference<V> implements Value<V>

從代碼5中可以看出,WeakCache對象中保持了對ClassLoader(包裝為CacheKey)和代理類(包裝為CacheValue)的弱引用,所以當此類加載器和代理類不再被強引用時,它們就會被回收。

存在的問題

然而,WeakCache的實現(xiàn)是有問題的,在java.lang.reflect.WeakCache.reverseMap和java.lang.reflect.WeakCache.valueFactory中的狀態(tài)在極限情況下可能會出現(xiàn)不同步,導致一個代理類被調(diào)用java.lang.reflect.Proxy.isProxyClass(Class<?>)的返回值不正確。具體可以參考RaceConditioninjava.lang.reflect.WeakCache。

不過這個問題在JDK9中已經(jīng)不存在了。

關(guān)于虛引用的GC行為

在上一節(jié),并沒有列出虛引用的使用場景,因為它的使用場景十分單一。PhantomReference設(shè)計的目的就是可以在對象被回收之前收到通知(通過注冊的隊列),所以它沒有不含注冊隊列的構(gòu)造器(只有publicPhantomReference(Treferent,ReferenceQueue<?superT>q),但你仍可以傳null進去),但這種場景在JDK里并沒有出現(xiàn),也很少有開發(fā)者使用它。

從PhantomReference類的源代碼可知,你永遠無法通過它獲取到它引用的那個對象(其get()方法永遠返回null),但是它又可以阻止其引用的目標對象被GC回收。從上文可知,通常一個不可達(強不可達、軟不可達、弱不可達)的對象會被finalize,然后被回收。但如果它在被回收前,GC發(fā)現(xiàn)它仍然是虛可達,那么它就不會回收這塊內(nèi)存,而這塊內(nèi)存又不能被訪問到,那么這塊內(nèi)存就泄漏了。

想要虛引用的「目標對象」被回收,必須讓「引用對象」本身不可達,或者顯式地清除虛引用。所以如果使用不當,很可能會造成內(nèi)存泄漏,這也是它使用范圍不廣的原因之一。

代碼6演示了這3種引用分別的GC行為:

//代碼6
private static List<PhantomReference<Object>> phantomRefs = new ArrayList<>();
private static List<WeakReference<Object>> weaks = new ArrayList<>();
private static List<SoftReference<Object>> softs = new ArrayList<>();

public static void testPhantomRefLeakOOM() {
  while(true) {
    //生成一個占用10M的內(nèi)存的對象
    Byte[] bytes = new Byte[1024 * 1024 * 10];
    //使用軟引用存儲
//  softs.add(new SoftReference<Object>(bytes));
    //使用虛引用存儲
    PhantomReference<Object> pf = new PhantomReference<Object>(bytes, null);
    //使用弱引用存儲
//  weaks.add((new WeakReference<Object>(bytes)));
    phantomRefs.add(pf);
    //顯式清除引用
//  pf.clear();
    //建議GC
    System.gc();
  }
}


以上代碼展示了4種影響GC的行為,分別是:

1. 使用軟引用的GC行為

GC日志如下,可以看到,當系統(tǒng)內(nèi)存不夠的時候(OOM之前),軟引用會被清除,引發(fā)GC,釋放內(nèi)存。

2017-07-03T12:36:22.995+0800: [Full GC (System.gc()) [PSYoungGen: 40971K->40960K(76288K)] [ParOldGen: 492061K->492061K(506880K)] 533033K->533022K(583168K), [Metaspace: 2727K->2727K(1056768K)], 0.0610620 secs] [Times: user=0.23 sys=0.00, real=0.06 secs] 

2017-07-03T12:36:24.391+0800: [Full GC (System.gc()) [PSYoungGen: 40960K->40960K(76288K)] [ParOldGen: 1065502K->1065502K(1087488K)] 1106462K->1106462K(1163776K), [Metaspace: 

2017-07-03T12:36:32.291+0800: [Full GC (System.gc()) [PSYoungGen: 40962K->40962K(76288K)] [ParOldGen: 2581022K->2581022K(2621952K)] 2621985K->2621985K(2698240K), [Metaspace: 2727K->2727K(1056768K)], 0.3106258 secs] [Times: user=2.31 sys=0.00, real=0.31 secs] 
2017-07-03T12:36:32.610+0800: [GC (System.gc()) [PSYoungGen: 40962K->128K(76288K)] 2662945K->2663070K(2739712K), 0.6298054 secs] [Times: user=4.63 sys=0.00, real=0.63 secs] 
2017-07-03T12:36:33.240+0800: [Full GC (System.gc()) [PSYoungGen: 128K->0K(76288K)] [ParOldGen: 2662942K->2662945K(2663424K)] 2663070K->2662945K(2739712K), [Metaspace: 2727K->2727K(1056768K)], 0.2898513 secs] [Times: user=2.25 sys=0.00, real=0.29 secs] 

2017-07-03T12:36:34.096+0800: [Full GC (System.gc()) [PSYoungGen: 40960K->40960K(76288K)] [ParOldGen: 2744865K->2744865K(2746368K)] 2785825K->2785825K(2822656K), [Metaspace: 2727K->2727K(1056768K)], 0.3282086 secs] [Times: user=2.47 sys=0.00, real=0.33 secs] 
2017-07-03T12:36:34.425+0800: [Full GC (Ergonomics) [PSYoungGen: 40960K->40960K(76288K)] [ParOldGen: 2744865K->2744865K(2777088K)] 2785825K->2785825K(2853376K), [Metaspace: 2727K->2727K(1056768K)], 0.3061587 secs] [Times: user=2.32 sys=0.00, real=0.31 secs] 


2017-07-03T12:36:34.731+0800: [Full GC (Allocation Failure) [PSYoungGen: 40960K->0K(76288K)] [ParOldGen: 2744865K->531K(225280K)] 2785825K->531K(301568K), [Metaspace: 2727K->2727K(1056768K)], 0.1559132 secs] [Times: user=0.02 sys=0.14, real=0.16 secs] 
2017-07-03T12:36:34.890+0800: [GC (System.gc()) [PSYoungGen: 40960K->32K(76288K)] 41491K->82483K(301568K), 0.0304114 secs] [Times: user=0.14 sys=0.00, real=0.03 secs] 
2017-07-03T12:36:34.920+0800: [Full GC (System.gc()) [PSYoungGen: 32K->0K(76288K)] [ParOldGen: 82451K->41491K(225280K)] 82483K->41491K(301568K), [Metaspace: 2727K->2727K(1056768K)], 0.0179676 secs] [Times: user=0.05 sys=0.00, real=0.02 secs] 
2017-07-03T12:36:34.941+0800: [GC (System.gc()) [PSYoungGen: 41649K->32K(76288K)] 83140K->123443K(301568K), 0.0323917 secs] [Times: user=0.11 sys=0.00, real=0.03 secs] 
2017-07-03T12:36:34.973+0800: [Full GC (System.gc()) [PSYoungGen: 32K->0K(76288K)] [ParOldGen: 123411K->82451K(225280K)] 123443K->82451K(301568K), [Metaspace: 2727K->2727K(1056768K)], 0.0424672 secs] [Times: user=0.20 sys=0.00, real=0.04 secs] 
2017-07-03T12:36:35.414+0800: [Full GC (System.gc()) [PSYoungGen: 41011K->40960K(76288K)] [ParOldGen: 287252K->287252K(308224K)] 328264K->328212K(384512K), [Metaspace: 2727K->2727K(1056768K)], 0.0520262 secs] [Times: user=0.33 sys=0.00, real=0.05 secs] 

2017-07-03T12:36:48.569+0800: [Full GC (Ergonomics) [PSYoungGen: 40960K->40960K(76288K)] [ParOldGen: 2744854K->2744854K(2777088K)] 2785815K->2785815K(2853376K), [Metaspace: 2727K->2727K(1056768K)], 0.3476025 secs] [Times: user=2.45 sys=0.02, real=0.35 secs] 
2017-07-03T12:36:48.916+0800: [Full GC (Allocation Failure) [PSYoungGen: 40960K->0K(76288K)] [ParOldGen: 2744854K->534K(444928K)] 2785815K->534K(521216K), [Metaspace: 2727K->2727K(1056768K)], 0.1644360 secs] [Times: user=0.02 sys=0.16, real=0.17 secs] 
2017-07-03T12:36:49.084+0800: [GC (System.gc()) [PSYoungGen: 40960K->32K(76288K)] 41494K->82486K(521216K), 0.0444057 secs] [Times: user=0.22 sys=0.00, real=0.04 secs] 
2017-07-03T12:36:49.128+0800: [Full GC (System.gc()) [PSYoungGen: 32K->0K(76288K)] [ParOldGen: 82454K->41494K(444928K)] 82486K->41494K(521216K), [Metaspace: 2727K->2727K(1056768K)], 0.0288512 secs] [Times: user=0.11 sys=0.00, real=0.03 secs]

2. 使用弱引用

GC日志如下,從中可以看到,弱引用所引用的目標對象,時時刻刻都在被GC。

2017-07-03T12:32:55.214+0800: [GC (System.gc()) [PSYoungGen: 43581K->728K(76288K)] 43581K->41696K(251392K), 0.0354037 secs] [Times: user=0.20 sys=0.00, real=0.04 secs] 
2017-07-03T12:32:55.252+0800: [Full GC (System.gc()) [PSYoungGen: 728K->0K(76288K)] [ParOldGen: 40968K->41502K(175104K)] 41696K->41502K(251392K), [Metaspace: 2726K->2726K(1056768K)], 0.0258447 secs] [Times: user=0.08 sys=0.00, real=0.03 secs] 

2017-07-03T12:32:55.533+0800: [Full GC (System.gc()) [PSYoungGen: 41309K->40960K(76288K)] [ParOldGen: 164381K->164381K(175104K)] 205690K->205341K(251392K), [Metaspace: 2726K->2726K(1056768K)], 0.0389489 secs] [Times: user=0.25 sys=0.00, real=0.04 secs] 

2017-07-03T12:32:57.413+0800: [Full GC (System.gc()) [PSYoungGen: 40960K->40960K(76288K)] [ParOldGen: 1024541K->1024541K(1046016K)] 1065502K->1065502K(1122304K), [Metaspace: 2726K->2726K(1056768K)], 0.1263574 secs] [Times: user=0.94 sys=0.00, real=0.13 secs] 

2017-07-03T12:33:05.364+0800: [Full GC (System.gc()) [PSYoungGen: 40962K->40962K(76288K)] [ParOldGen: 2581022K->2581022K(2621952K)] 2621984K->2621984K(2698240K), [Metaspace: 2726K->2726K(1056768K)], 0.2474419 secs] [Times: user=1.69 sys=0.00, real=0.25 secs] 

2017-07-03T12:33:07.447+0800: [Full GC (Ergonomics) [PSYoungGen: 40960K->40960K(76288K)] [ParOldGen: 2744864K->2744864K(2777088K)] 2785824K->2785824K(2853376K), [Metaspace: 2726K->2726K(1056768K)], 0.2825105 secs] [Times: user=1.79 sys=0.00, real=0.28 secs] 
2017-07-03T12:33:07.729+0800: [Full GC (Allocation Failure) [PSYoungGen: 40960K->40960K(76288K)] [ParOldGen: 2744864K->2744851K(2777088K)] 2785824K->2785812K(2853376K), [Metaspace: 2726K->2726K(1056768K)], 0.8902204 secs]Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
  at me.lk.TestReference.testPhantomRefLeakOOM(TestReference.java:109)
  at me.lk.TestReference.main(TestReference.java:50)
 [Times: user=3.79 sys=0.00, real=0.89 secs] 
Heap
 PSYoungGen   total 76288K, used 43025K [0x000000076b400000, 0x0000000770900000, 0x00000007c0000000)
 eden space 65536K, 65% used [0x000000076b400000,0x000000076de04408,0x000000076f400000)
 from space 10752K, 0% used [0x000000076f400000,0x000000076f400000,0x000000076fe80000)
 to  space 10752K, 0% used [0x000000076fe80000,0x000000076fe80000,0x0000000770900000)
 ParOldGen    total 2777088K, used 2744851K [0x00000006c1c00000, 0x000000076b400000, 0x000000076b400000)
 object space 2777088K, 98% used [0x00000006c1c00000,0x0000000769484fb8,0x000000076b400000)
 Metaspace    used 2757K, capacity 4490K, committed 4864K, reserved 1056768K
 class space  used 310K, capacity 386K, committed 512K, reserved 1048576K

3. 使用虛引用,不顯式清除

GC日志如下,可以看到,不顯式清除的虛引用會阻止GC回收內(nèi)存,最終導致OOM。

2017-07-03T12:32:55.214+0800: [GC (System.gc()) [PSYoungGen: 43581K->728K(76288K)] 43581K->41696K(251392K), 0.0354037 secs] [Times: user=0.20 sys=0.00, real=0.04 secs] 
2017-07-03T12:32:55.252+0800: [Full GC (System.gc()) [PSYoungGen: 728K->0K(76288K)] [ParOldGen: 40968K->41502K(175104K)] 41696K->41502K(251392K), [Metaspace: 2726K->2726K(1056768K)], 0.0258447 secs] [Times: user=0.08 sys=0.00, real=0.03 secs] 

2017-07-03T12:32:55.533+0800: [Full GC (System.gc()) [PSYoungGen: 41309K->40960K(76288K)] [ParOldGen: 164381K->164381K(175104K)] 205690K->205341K(251392K), [Metaspace: 2726K->2726K(1056768K)], 0.0389489 secs] [Times: user=0.25 sys=0.00, real=0.04 secs] 

2017-07-03T12:32:57.413+0800: [Full GC (System.gc()) [PSYoungGen: 40960K->40960K(76288K)] [ParOldGen: 1024541K->1024541K(1046016K)] 1065502K->1065502K(1122304K), [Metaspace: 2726K->2726K(1056768K)], 0.1263574 secs] [Times: user=0.94 sys=0.00, real=0.13 secs] 

2017-07-03T12:33:05.364+0800: [Full GC (System.gc()) [PSYoungGen: 40962K->40962K(76288K)] [ParOldGen: 2581022K->2581022K(2621952K)] 2621984K->2621984K(2698240K), [Metaspace: 2726K->2726K(1056768K)], 0.2474419 secs] [Times: user=1.69 sys=0.00, real=0.25 secs] 

2017-07-03T12:33:07.447+0800: [Full GC (Ergonomics) [PSYoungGen: 40960K->40960K(76288K)] [ParOldGen: 2744864K->2744864K(2777088K)] 2785824K->2785824K(2853376K), [Metaspace: 2726K->2726K(1056768K)], 0.2825105 secs] [Times: user=1.79 sys=0.00, real=0.28 secs] 
2017-07-03T12:33:07.729+0800: [Full GC (Allocation Failure) [PSYoungGen: 40960K->40960K(76288K)] [ParOldGen: 2744864K->2744851K(2777088K)] 2785824K->2785812K(2853376K), [Metaspace: 2726K->2726K(1056768K)], 0.8902204 secs]Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
  at me.lk.TestReference.testPhantomRefLeakOOM(TestReference.java:109)
  at me.lk.TestReference.main(TestReference.java:50)
 [Times: user=3.79 sys=0.00, real=0.89 secs] 
Heap
 PSYoungGen   total 76288K, used 43025K [0x000000076b400000, 0x0000000770900000, 0x00000007c0000000)
 eden space 65536K, 65% used [0x000000076b400000,0x000000076de04408,0x000000076f400000)
 from space 10752K, 0% used [0x000000076f400000,0x000000076f400000,0x000000076fe80000)
 to  space 10752K, 0% used [0x000000076fe80000,0x000000076fe80000,0x0000000770900000)
 ParOldGen    total 2777088K, used 2744851K [0x00000006c1c00000, 0x000000076b400000, 0x000000076b400000)
 object space 2777088K, 98% used [0x00000006c1c00000,0x0000000769484fb8,0x000000076b400000)
 Metaspace    used 2757K, capacity 4490K, committed 4864K, reserved 1056768K
 class space  used 310K, capacity 386K, committed 512K, reserved 1048576K

4. 使用虛引用,顯式清除

顯式清除的虛引用,不會影響GC,其GC行為和弱引用十分相似。

2017-07-03T12:45:14.774+0800: [GC (System.gc()) [PSYoungGen: 43581K->696K(76288K)] 43581K->41664K(251392K), 0.0458469 secs] [Times: user=0.17 sys=0.00, real=0.05 secs] 
2017-07-03T12:45:14.820+0800: [Full GC (System.gc()) [PSYoungGen: 696K->0K(76288K)] [ParOldGen: 40968K->41502K(175104K)] 41664K->41502K(251392K), [Metaspace: 2726K->2726K(1056768K)], 0.0198788 secs] [Times: user=0.08 sys=0.00, real=0.02 secs] 
2017-07-03T12:45:14.842+0800: [GC (System.gc()) [PSYoungGen: 42231K->32K(76288K)] 83734K->82495K(251392K), 0.0367363 secs] [Times: user=0.22 sys=0.00, real=0.04 secs] 
2017-07-03T12:45:14.879+0800: [Full GC (System.gc()) [PSYoungGen: 32K->0K(76288K)] [ParOldGen: 82463K->41501K(175104K)] 82495K->41501K(251392K), [Metaspace: 2727K->2727K(1056768K)], 0.0198085 secs] [Times: user=0.08 sys=0.00, real=0.02 secs] 
2017-07-03T12:45:14.901+0800: [GC (System.gc()) [PSYoungGen: 41786K->32K(76288K)] 83287K->82493K(251392K), 0.0327529 secs] [Times: user=0.19 sys=0.00, real=0.03 secs] 
2017-07-03T12:45:14.934+0800: [Full GC (System.gc()) [PSYoungGen: 32K->0K(76288K)] [ParOldGen: 82461K->41501K(175104K)] 82493K->41501K(251392K), [Metaspace: 2727K->2727K(1056768K)], 0.0283782 secs] [Times: user=0.17 sys=0.00, real=0.03 secs] 
2017-07-03T12:45:14.964+0800: [GC (System.gc()) [PSYoungGen: 41497K->32K(76288K)] 82998K->82493K(251392K), 0.0336216 secs] [Times: user=0.20 sys=0.00, real=0.03 secs] 
2017-07-03T12:45:14.998+0800: [Full GC (System.gc()) [PSYoungGen: 32K->0K(76288K)] [ParOldGen: 82461K->41501K(175104K)] 82493K->41501K(251392K), [Metaspace: 2727K->2727K(1056768K)], 0.0211702 secs] [Times: user=0.13 sys=0.00, real=0.02 secs] 
2017-07-03T12:45:15.021+0800: [GC (System.gc()) [PSYoungGen: 41309K->32K(76288K)] 82810K->82493K(251392K), 0.0445368 secs] [Times: user=0.30 sys=0.00, real=0.05 secs] 
2017-07-03T12:45:15.066+0800: [Full GC (System.gc()) [PSYoungGen: 32K->0K(76288K)] [ParOldGen: 82461K->41501K(175104K)] 82493K->41501K(251392K), [Metaspace: 2727K->2727K(1056768K)], 0.0219968 secs] [Times: user=0.11 sys=0.00, real=0.02 secs] 
2017-07-03T12:45:15.090+0800: [GC (System.gc()) [PSYoungGen: 41186K->32K(76288K)] 82688K->82493K(251392K), 0.0436528 secs] [Times: user=0.36 sys=0.00, real=0.04 secs] 
2017-07-03T12:45:15.133+0800: [Full GC (System.gc()) [PSYoungGen: 32K->0K(76288K)] [ParOldGen: 82461K->41501K(175104K)] 82493K->41501K(251392K), [Metaspace: 2727K->2727K(1056768K)], 0.0219814 secs] [Times: user=0.11 sys=0.00, real=0.02 secs]

總結(jié)

以上就是本文關(guān)于Java中的引用和動態(tài)代理的實現(xiàn)詳解的全部內(nèi)容,希望對大家有所幫助。感興趣的朋友可以繼續(xù)啊參閱本站:

淺談Java注解和動態(tài)代理

Java使用代理進行網(wǎng)絡連接方法示例

Java設(shè)計模式之代理模式原理及實現(xiàn)代碼分享

如有不足之處,歡迎留言指出。感謝朋友們對本站的支持。

相關(guān)文章

  • Java Reactor反應器模式使用方法詳解

    Java Reactor反應器模式使用方法詳解

    這篇文章主要介紹了Java Reactor反應器模式使用方法詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-06-06
  • Java實現(xiàn)輸出數(shù)字三角形實例代碼

    Java實現(xiàn)輸出數(shù)字三角形實例代碼

    大家好,本篇文章主要講的是Java實現(xiàn)輸出三角形實例代碼,感興趣的同學趕快來看一看吧,對你有幫助的話記得收藏一下,方便下次瀏覽
    2022-01-01
  • 詳解Spring依賴注入的三種方式以及優(yōu)缺點

    詳解Spring依賴注入的三種方式以及優(yōu)缺點

    IoC?和?DI?是?Spring?中最重要的兩個概念,其中?IoC(Inversion?of?Control)為控制反轉(zhuǎn)的思想,而?DI(Dependency?Injection)依賴注入為其(IoC)具體實現(xiàn)。那么?DI?實現(xiàn)依賴注入的方式有幾種?這些注入方式又有什么不同?本文就來和大家一起詳細聊聊
    2022-08-08
  • Java網(wǎng)絡編程UDP協(xié)議發(fā)送接收數(shù)據(jù)

    Java網(wǎng)絡編程UDP協(xié)議發(fā)送接收數(shù)據(jù)

    這篇文章主要為大家詳細介紹了Java網(wǎng)絡編程UDP協(xié)議發(fā)送接收數(shù)據(jù),文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-08-08
  • Java?SWT中常見彈出框?qū)嵗偨Y(jié)

    Java?SWT中常見彈出框?qū)嵗偨Y(jié)

    剛開始寫Java工具的小伙伴可能不知道怎么寫消息對話框,在這里總結(jié)一些常用的幾種消息彈出框,下面這篇文章主要給大家介紹了關(guān)于Java?SWT中常見彈出框的相關(guān)資料,需要的朋友可以參考下
    2023-01-01
  • SpringCloud實現(xiàn)SSO 單點登錄的示例代碼

    SpringCloud實現(xiàn)SSO 單點登錄的示例代碼

    作為分布式項目,單點登錄是必不可少的,這篇文章主要介紹了SpringCloud實現(xiàn)SSO 單點登錄的示例代碼,非常具有實用價值,需要的朋友可以參考下
    2019-01-01
  • Java返回文件時為圖片或pdf等設(shè)置在線預覽或下載功能

    Java返回文件時為圖片或pdf等設(shè)置在線預覽或下載功能

    這篇文章主要介紹了Java返回文件時為圖片或pdf等設(shè)置在線預覽或下載功能,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友參考下吧
    2024-01-01
  • Spring Cloud Config工作原理概述

    Spring Cloud Config工作原理概述

    Spring Cloud Config 是 Spring Cloud 生態(tài)系統(tǒng)的一部分,它提供了一種集中化管理應用配置的方法,本文給大家介紹Spring Cloud Config工作原理概述,感興趣的朋友跟隨小編一起看看吧
    2024-08-08
  • SpringBoot實現(xiàn)MD5加鹽算法的示例代碼

    SpringBoot實現(xiàn)MD5加鹽算法的示例代碼

    加鹽算法是一種用于增強密碼安全性的技術(shù),本文主要介紹了SpringBoot實現(xiàn)MD5加鹽算法的示例代碼,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2025-03-03
  • Java線程中Thread方法下的Join方法詳解

    Java線程中Thread方法下的Join方法詳解

    這篇文章主要介紹了Java線程中Thread方法下的Join方法詳解,在項目中往往會遇到這樣一個場景,就是需要等待幾件事情都給做完后才能走下面的事情,這個時候就需要用到Thread方法下的Join方法,join方法是無參且沒有返回值的,需要的朋友可以參考下
    2024-01-01

最新評論

新宁县| 肥东县| 东台市| 乌鲁木齐市| 阳东县| 石阡县| 康定县| 瑞安市| 来凤县| 陵川县| 井冈山市| 大化| 竹溪县| 杂多县| 东源县| 扎鲁特旗| 安阳市| 武山县| 武隆县| 乐山市| 新乡市| 青州市| 石屏县| 奎屯市| 囊谦县| 当阳市| 广平县| 崇州市| 黄陵县| 北海市| 禹城市| 焦作市| 民县| 海南省| 浮山县| 贵南县| 濉溪县| 蕲春县| 海兴县| 毕节市| 博罗县|