国产片侵犯亲女视频播放_亚洲精品二区_在线免费国产视频_欧美精品一区二区三区在线_少妇久久久_在线观看av不卡

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - Java中七個潛在的內存泄露風險,你知道幾個?

Java中七個潛在的內存泄露風險,你知道幾個?

2021-03-23 01:09苦味代碼L Java教程

雖然Java程序員不用像C/C++程序員那樣時刻關注內存的使用情況,JVM會幫我們處理好這些,但并不是說有了GC就可以高枕無憂,內存泄露相關的問題一般在測試的時候很難發現,一旦上線流量起來可能馬上就是一個詭異的線上故障。

雖然Java程序員不用像C/C++程序員那樣時刻關注內存的使用情況,JVM會幫我們處理好這些,但并不是說有了GC就可以高枕無憂,內存泄露相關的問題一般在測試的時候很難發現,一旦上線流量起來可能馬上就是一個詭異的線上故障。

Java中七個潛在的內存泄露風險,你知道幾個?

1. 內存泄露的定義

如果GC無法回收內存中不再使用的對象,則定義為內存有泄露

2. 未關閉的資源類

當我們在程序中打開一個新的流或者是新建一個網絡連接的時候,JVM都會為這些資源類分配內存做緩存,常見的資源類有網絡連接,數據庫連接以及IO流。值得注意的是,如果在業務處理中異常,則有可能導致程序不能執行關閉資源類的代碼,因此最好按照下面的做法處理資源類

public void handleResource() { 

    try { 

        // open connection 

        // handle business 

    } catch (Throwable t) { 

        // log stack 

    } finally { 

        // close connection 

    } 

3. 未正確實現equals()和hashCode()

假如有下面的這個類

public class Person { 

    public String name

     

    public Person(String name) { 

        this.name = name

    } 

并且如果在程序中有下面的操作

@Test 

public void givenMapWhenEqualsAndHashCodeNotOverriddenThenMemoryLeak() { 

    Map<Person, Integer> map = new HashMap<>(); 

    for(int i=0; i<100; i++) { 

        map.put(new Person("jon"), 1); 

    } 

    Assert.assertFalse(map.size() == 1); 

可以預見,這個單元測試并不能通過,原因是Person類沒有實現equals方法,因此使用Object的equals方法,直接比較實體對象的地址,所以map.size() == 100

如果我們改寫Person類的代碼如下所示:

public class Person { 

    public String name

     

    public Person(String name) { 

        this.name = name

    } 

     

    @Override 

    public boolean equals(Object o) { 

        if (o == this) return true

        if (!(o instanceof Person)) { 

            return false

        } 

        Person person = (Person) o; 

        return person.name.equals(name); 

    } 

     

    @Override 

    public int hashCode() { 

        int result = 17; 

        result = 31 * result + name.hashCode(); 

        return result; 

    } 

則上文中的單元測試就可以順利通過了,需要注意的是這個場景比較隱蔽,一定要在平時的代碼中注意。

4. 非靜態內部類

要知道,所有的非靜態類別類都持有外部類的引用,因此某些情況如果引用內部類可能延長外部類的生命周期,甚至持續到進程結束都不能回收外部類的空間,這類內存溢出一般在Android程序中比較多,只要MyAsyncTask處于運行狀態MainActivity的內存就釋放不了,很多時候安卓開發者這樣做只是為了在內部類中拿到外部類的屬性,殊不知,此時內存已經泄露了。

public class MainActivity extends Activity { 

    @Override 

    protected void onCreate(Bundle savedInstanceState) { 

        super.onCreate(savedInstanceState); 

        setContentView(R.layout.main); 

        new MyAsyncTask().execute(); 

    } 

 

    private class MyAsyncTask extends AsyncTask { 

        @Override 

        protected Object doInBackground(Object[] params) { 

            return doSomeStuff(); 

        } 

        private Object doSomeStuff() { 

            //do something to get result 

            return new MyObject(); 

        } 

    } 

5. 重寫了finalize()的類

如果運行下面的這個例子,則最終程序會因為OOM的原因崩潰

public class Finalizer { 

    @Override 

    protected void finalize() throws Throwable { 

    while (true) { 

           Thread.yield(); 

      } 

  } 

 

public static void main(String str[]) { 

  while (true) { 

        for (int i = 0; i < 100000; i++) { 

            Finalizer force = new Finalizer(); 

        } 

   } 

 } 

JVM對重寫了finalize()的類的處理稍微不同,首先會針對這個類創建一個java.lang.ref.Finalizer類,并讓java.lang.ref.Finalizer持有這個類的引用,在上文中的例子中,因為Finalizer類的引用被java.lang.ref.Finalizer持有,所以他的實例并不能被Young GC清理,反而會轉入到老年代。在老年代中,JVM GC的時候會發現Finalizer類只被java.lang.ref.Finalizer引用,因此將其標記為可GC狀態,并放入到java.lang.ref.Finalizer.ReferenceQueue這個隊列中。等到所有的Finalizer類都加到隊列之后,JVM會起一個后臺線程去清理java.lang.ref.Finalizer.ReferenceQueue中的對象,之后這個后臺線程就專門負責清理java.lang.ref.Finalizer.ReferenceQueue中的對象了。這個設計看起來是沒什么問題的,但其實有個坑,那就是負責清理java.lang.ref.Finalizer.ReferenceQueue的后臺線程優先級是比較低的,并且系統沒有提供可以調節這個線程優先級的接口或者配置。因此當我們在使用使用重寫finalize()方法的對象時,千萬不要瞬間產生大量的對象,要時刻謹記,JVM對此類對象的處理有特殊邏輯。

6. 針對長字符串調用String.intern()

如果提前在src/test/resources/large.txt中寫入大量字符串,并且在Java 1.6及以下的版本運行下面程序,也將得到一個OOM

@Test 

public void givenLengthString_whenIntern_thenOutOfMemory() 

  throws IOException, InterruptedException { 

    String str  

      = new Scanner(new File("src/test/resources/large.txt"), "UTF-8"

      .useDelimiter("\\A").next(); 

    str.intern(); 

     

    System.gc();  

    Thread.sleep(15000); 

原因是在Java 1.6及以下,字符串常量池是處于JVM的PermGen區的,并且在程序運行期間不會GC,因此產生了OOM。在Java 1.7以及之后字符串常量池轉移到了HeapSpace此類問題也就無需再關注了

7. ThreadLocal的誤用

ThreadLocal一定要列在Java內存泄露的榜首,總能在不知不覺中將內存泄露掉,一個常見的例子是:

@Test 

public void testThreadLocalMemoryLeaks() { 

    ThreadLocal<List<Integer>> localCache = new ThreadLocal<>(); 

   List<Integer> cacheInstance = new ArrayList<>(10000); 

    localCache.set(cacheInstance); 

    localCache = new ThreadLocal<>(); 

當localCache的值被重置之后cacheInstance被ThreadLocalMap中的value引用,無法被GC,但是其key對ThreadLocal實例的引用是一個弱引用,本來ThreadLocal的實例被localCache和ThreadLocalMap的key同時引用,但是當localCache的引用被重置之后,則ThreadLocal的實例只有ThreadLocalMap的key這樣一個弱引用了,此時這個實例在GC的時候能夠被清理。

Java中七個潛在的內存泄露風險,你知道幾個?

img

其實看過ThreadLocal源碼的同學會知道,ThreadLocal本身對于key為null的Entity有自清理的過程,但是這個過程是依賴于后續對ThreadLocal的繼續使用,假如上面的這段代碼是處于一個秒殺場景下,會有一個瞬間的流量峰值,這個流量峰值也會將集群的內存打到高位(或者運氣不好的話直接將集群內存打滿導致故障),后面由于峰值流量已過,對ThreadLocal的調用也下降,會使得ThreadLocal的自清理能力下降,造成內存泄露。ThreadLocal的自清理實現是錦上添花,千萬不要指望它雪中送碳。

8. 類的靜態變量

Tomcat對在網絡容器中使用ThreadLocal引起的內存泄露做了一個總結,詳見:https://cwiki.apache.org/confluence/display/tomcat/MemoryLeakProtection,這里我們列舉其中的一個例子。

熟悉Tomcat的同學知道,Tomcat中的web應用由webapp classloader這個類加載器的,并且webapp classloader是破壞雙親委派機制實現的,即所有的web應用先由webapp classloader加載,這樣的好處就是可以讓同一個容器中的web應用以及依賴隔離。

下面我們看具體的內存泄露的例子:

public class MyCounter { 

 private int count = 0; 

 

 public void increment() { 

  count++; 

 } 

 

 public int getCount() { 

  return count

 } 

 

public class MyThreadLocal extends ThreadLocal<MyCounter> { 

 

public class LeakingServlet extends HttpServlet { 

 private static MyThreadLocal myThreadLocal = new MyThreadLocal(); 

 

 protected void doGet(HttpServletRequest request, 

   HttpServletResponse response) throws ServletException, IOException { 

 

  MyCounter counter = myThreadLocal.get(); 

  if (counter == null) { 

   counter = new MyCounter(); 

   myThreadLocal.set(counter); 

  } 

 

  response.getWriter().println( 

    "The current thread served this servlet " + counter.getCount() 

      + " times"); 

  counter.increment(); 

 } 

需要注意這個例子中的兩個非常關鍵的點:

  • MyCounter以及MyThreadLocal必須放到web應用的路徑中,保被webapp classloader加載
  • ThreadLocal類一定得是ThreadLocal的繼承類,比如例子中的MyThreadLocal,因為ThreadLocal本來被common classloader加載,其生命周期與tomcat容器一致。ThreadLocal的繼承類包括比較常見的NamedThreadLocal,注意不要踩坑。

假如LeakingServlet所在的web應用啟動,MyThreadLocal類也會被webapp classloader加載,如果此時web應用下線,而線程的生命周期未結束(比如為LeakingServlet提供服務的線程是一個線程池中的線程),那會導致myThreadLocal的實例仍然被這個線程引用,而不能被GC,期初看來這個帶來的問題也不大,因為myThreadLocal所引用的對象占用的內存空間不太多,問題在于myThreadLocal間接持有加載web應用的webapp classloader的引用(通過myThreadLocal.getClass().getClassLoader()可以引用到),而加載web應用的webapp classloader有持有它加載的所有類的引用,這就引起了classloader泄露,它泄露的內存就非常可觀了。

原文地址:https://mp.weixin.qq.com/s?__biz=Mzk0NjExMjU3Mg==&mid=2247484269&idx=1&sn=3c2ffe355bd2f0890411e27ac3f84e90&chksm=c30a523ef47ddb284eb54872d5fd10b96f41762e049ae451daa649e7960bcdfdb15d44117035&mpshare=1&

延伸 · 閱讀

精彩推薦
Weibo Article 1 Weibo Article 2 Weibo Article 3 Weibo Article 4 Weibo Article 5 Weibo Article 6 Weibo Article 7 Weibo Article 8 Weibo Article 9 Weibo Article 10 Weibo Article 11 Weibo Article 12 Weibo Article 13 Weibo Article 14 Weibo Article 15 Weibo Article 16 Weibo Article 17 Weibo Article 18 Weibo Article 19 Weibo Article 20 Weibo Article 21 Weibo Article 22 Weibo Article 23 Weibo Article 24 Weibo Article 25 Weibo Article 26 Weibo Article 27 Weibo Article 28 Weibo Article 29 Weibo Article 30 Weibo Article 31 Weibo Article 32 Weibo Article 33 Weibo Article 34 Weibo Article 35 Weibo Article 36 Weibo Article 37 Weibo Article 38 Weibo Article 39 Weibo Article 40
主站蜘蛛池模板: 久久99视频这里只有精品 | 99久久免费看精品国产 | 欧美一级在线观看 | 亚洲欧洲精品成人久久奇米网 | 欧美浮力 | 午夜爽视频 | 日韩一区二区精品 | jizzz日本| 黄色高清网站 | 一级毛片免费一级 | av大片 | 成人美女av | 北条麻妃一区二区三区在线观看 | 欧美aⅴ| 午夜激情影院 | 成人黄色免费在线视频 | 欧美一区二区三区黄色 | 国产成人精品电影 | 精品成人一区二区 | 欧美一级一 | 精品国产一区二区三区久久久 | 欧美天堂 | 亚洲尤物 | 99精品久久久| 日本中文字幕在线免费观看 | 亚洲网站免费 | 最新电影在线高清免费完整观看视频 | 国产精品久久久久久亚洲调教 | 黄网站色| 欧美亚洲免费 | 日韩精品久久 | 一级毛片一级毛片一级毛片 | 天天爽夜夜爽夜夜爽精品视频 | 综合色网站 | av免费人人干 | 亚洲国产成人精品久久久国产成人一区 | 日本一区二区三区精品视频 | 成人免费小视频 | 日本一区二区高清视频 | 国产高清免费 | 一本久久a久久精品亚洲 |