Netty堆外內存洩漏排查,這一篇全講清楚了

2021-02-07 Java筆記蝦

上篇文章介紹了Netty內存模型原理,由於Netty使用不當會導致堆外內存洩漏,網上關於這方面的資料比較少,所以寫下這篇文章,基於Netty4.1.43.Final,專門介紹排查Netty堆外內存相關的知識點,診斷工具,以及排查思路

現象

堆外內存洩漏的現象主要是,進程佔用的內存較高(Linux下可以用top命令查看),但Java堆內存佔用並不高(jmap命令查看),常見的使用堆外內存除了Netty,還有基於java.nio下相關接口申請堆外內存,JNI調用等,下面側重介紹Netty堆外內存洩漏問題排查

堆外內存釋放底層實現1 java.nio堆外內存釋放

Netty堆外內存是基於原生java.nio的DirectByteBuffer對象的基礎上實現的,所以有必要先了解下它的釋放原理

java.nio提供的DirectByteBuffer提供了sun.misc.Cleaner類的clean()方法,進行系統調用釋放堆外內存,觸發clean()方法的情況有2種

ByteBuffer buf = ByteBuffer.allocateDirect(1);
((DirectBuffer) byteBuffer).cleaner().clean();

Cleaner類繼承了java.lang.ref.Reference,GC線程會通過設置Reference的內部變量(pending變量為鍊表頭部節點,discovered變量為下一個鍊表節點),將可被回收的不可達的Reference對象以鍊表的方式組織起來

Reference的內部守護線程從鍊表的頭部(head)消費數據,如果消費到的Reference對象同時也是Cleaner類型,線程會調用clean()方法(Reference#tryHandlePending())

2 Netty noCleaner策略

介紹noCleaner策略之前,需要先理解帶有Cleaner對象的DirectByteBuffer在初始化時做了哪些事情:

只有在DirectByteBuffer(int cap)構造方法中才會初始化Cleaner對象,方法中檢查當前內存是否超過允許的最大堆外內存(可由-XX:MaxDirectMemorySize配置)

如果超出,則會先嘗試將不可達的Reference對象加入Reference鍊表中,依賴Reference的內部守護線程觸發可以被回收DirectByteBuffer關聯的Cleaner的run()方法

如果內存還是不足, 則執行 System.gc(),觸發full gc,來回收堆內存中的DirectByteBuffer對象來觸發堆外內存回收,如果還是超過限制,則拋出java.lang.OutOfMemoryError(代碼位於java.nio.Bits#reserveMemory()方法)

而Netty在4.1引入可以noCleaner策略:創建不帶Cleaner的DirectByteBuffer對象,這樣做的好處是繞開帶Cleaner的DirectByteBuffer執行構造方法和執行Cleaner的clean()方法中一些額外開銷,當堆外內存不夠的時候,不會觸發System.gc(),提高性能

hasCleaner的DirectByteBuffer和noCleaner的DirectByteBuffer主要區別如下:

構造器方式不同:noCleaner對象:由反射調用 private DirectByteBuffer(long addr, int cap)創建hasCleaner對象:由 new DirectByteBuffer(int cap)創建

釋放內存的方式不同noCleaner對象:使用 UnSafe.freeMemory(address);hasCleaner對象:使用 DirectByteBuffer 的 Cleaner 的 clean() 方法

note:Unsafe是位於sun.misc包下的一個類,可以提供內存操作、對象操作、線程調度等本地方法,這些方法在提升Java運行效率、增強Java語言底層資源操作能力方面起到了很大的作用,但不正確使用Unsafe類會使得程序出錯的概率變大,程序不再「安全」,因此官方不推薦使用,並可能在未來的jdk版本移除

Netty在啟動時需要判斷檢查當前環境、環境配置參數是否允許noCleaner策略(具體邏輯位於PlatformDependent的static代碼塊),例如運行在Android下時,是沒有Unsafe類的,不允許使用noCleaner策略,如果不允許,則使用hasCleaner策略

note:可以通過調用Netty的PlatformDependent.useDirectBufferNoCleaner()方法查看當前Netty程序是否使用noCleaner策略

ByteBuf.release()的觸發

業界有一種誤解認為 Netty 框架分配的 ByteBuf,框架會自動釋放,業務不需要釋放;業務創建的 ByteBuf 則需要自己釋放,Netty 框架不會釋放

產生這種誤解是有原因的,Netty框架是會在一些場景調用ByteBuf.release()方法:

1 入站消息處理

當處理入站消息時,Netty會創建ByteBuf讀取channel上的消息,並觸發調用pipeline上的ChannelHandler處理,應用程式定義的使用ByteBuf的ChannelHandler需要負責release()

public void channelRead(ChannelHandlerContext ctx, Object msg) {
ByteBuf buf = (ByteBuf) msg;
 try {
   ...
 } finally {
   buf.release();
 }
}

如果該ByteBuf不由當前ChannelHandler處理,則傳遞給pipeline上下一個handler:

public void channelRead(ChannelHandlerContext ctx, Object msg) {
 ByteBuf buf = (ByteBuf) msg;
 ...
 ctx.fireChannelRead(buf);
}

常用的我們會通過繼承ChannelInboundHandlerAdapter定義入站消息處理的handler,這種情況下如果所有程序的hanler都沒有調用release()方法,該入站消息Netty最後並不會release(),會導致內存洩漏

當在pipeline的handler處理中拋出異常之後,最後Netty框架是會捕捉該異常進行ByteBuf.release()的;完整流程位於AbstractNioByteChannel.NioByteUnsafe#read(),下面抽取關鍵片段:

try {
do {
byteBuf = allocHandle.allocate(allocator);
allocHandle.lastBytesRead(doReadBytes(byteBuf));
// 入站消息已讀完
if (allocHandle.lastBytesRead() <= 0) {
// ...
break;
}
// 觸發pipline上handler進行處理
pipeline.fireChannelRead(byteBuf);
byteBuf = null;
} while (allocHandle.continueReading());
// ...
} catch (Throwable t) {
// 異常處理中包括調用 byteBuf.release()
handleReadException(pipeline, byteBuf, t, close, allocHandle);
}

不過,常用的還有通過繼承SimpleChannelInboundHandler定義入站消息處理,在該類會保證消息最終被release:

@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
boolean release = true;
try {
// 該消息由當前handler處理
if (acceptInboundMessage(msg)) {
I imsg = (I) msg;
channelRead0(ctx, imsg);
} else {
// 不由當前handler處理,傳遞給pipeline上下一個handler
release = false;
ctx.fireChannelRead(msg);
}
} finally {
// 觸發release
if (autoRelease && release) {
ReferenceCountUtil.release(msg);
}
}
}

2 出站消息處理

不同於入站消息是由Netty框架自動創建的,出站消息通常由應用程式創建,然後調用基於channel的write()方法或writeAndFlush()方法,這些方法內部會負責調用傳入的byteBuf的release()方法

note: write()方法在netty-4.0.0.CR2前的版本存在問題,不會調用ByteBuf.release()

3 release()注意事項

還有一種常見的誤解就是,只要調用了ByteBuf的release()方法,或者調用ReferenceCountUtil.release()方法,對象的內存就保證釋放了,其實不是

因為Netty的ByteBuf引用計數來管理ByteBuf對象的生命周期,ByteBuf繼承了ReferenceCounted接口,對外提供retain()和release()方法,用於增加或減少引用計數值,當調用release()方法時,內部計數值被減為0才會觸發內存回收動作

derived,派生的意思,ByteBuf.duplicate(), ByteBuf.slice() 和 ByteBuf.order(ByteOrder) 等方法會創建出derived  ByteBuf,創建出來的ByteBuf與原有ByteBuf是共享引用計數的,原有ByteBuf的release()方法調用,也會導致這些對象內存回收

相反ByteBuf.copy() 和 ByteBuf.readBytes(int)方法創建出來的對象並不是derived ByteBuf,這些對象與原有ByteBuf不是共享引用計數的,原有ByteBuf的release()方法調用不會導致這些對象內存回收

堆外內存大小控制參數

配置堆外內存大小的參數有JVM的-XX:MaxDirectMemorySize和Netty的-Dio.netty.maxDirectMemory,這2個參數有什麼區別?

用於限制Netty中hasCleaner策略的DirectByteBuffer堆外內存的大小,默認值是JVM能從作業系統申請的最大內存,如果內存本身沒限制,則值為Long.MAX_VALUE個字節(默認值由Runtime.getRuntime().maxMemory()返回),代碼位於java.nio.Bits#reserveMemory()方法中

note:-XX:MaxDirectMemorySize無法限制Netty中noCleaner策略的DirectByteBuffer堆外內存的大小

-Dio.netty.maxDirectMemory用於限制noCleaner策略下Netty的DirectByteBuffer分配的最大堆外內存的大小,如果該值為0,則使用hasCleaner策略,代碼位於PlatformDependent#incrementMemoryCounter()方法中堆外內存監控

如何獲取堆外內存的使用情況?

1 代碼工具(1) hasCleaner的DirectByteBuffer監控對於hasCleaner策略的DirectByteBuffer,java.nio.Bits類是有記錄堆外內存的使用情況,但是該類是包級別的訪問權限,不能直接獲取,可以通過MXBean來獲取

note:MXBean,Java提供的一系列用於監控統計的特殊Bean,通過不同類型的MXBean可以獲取JVM進程的內存,線程、類加載信息等監控指標

List<BufferPoolMXBean> bufferPoolMXBeans = ManagementFactoryHelper.getBufferPoolMXBeans();
BufferPoolMXBean directBufferMXBean = bufferPoolMXBeans.get(0);

long count = directBufferMXBean.getCount();

long memoryUsed = directBufferMXBean.getMemoryUsed();

note: MappedByteBuffer:是基於FileChannelImpl.map進行進行mmap內存映射(零拷貝的一種實現)得到的另外一種堆外內存的ByteBuffer,可以通過ManagementFactoryHelper.getBufferPoolMXBeans().get(1)獲取到該堆外內存的監控指標

(2) noCleaner的DirectByteBuffer監控Netty中noCleaner的DirectByteBuffer的監控比較簡單,直接通過PlatformDependent.usedDirectMemory()訪問即可2 Netty自帶內存洩漏檢測工具

Netty也自帶了內存洩漏檢測工具,可用於檢測出ByteBuf對象被GC回收,但ByteBuf管理的內存沒有釋放的情況,但不適用ByteBuf對象還沒被GC回收內存洩漏的情況,例如任務隊列積壓

為了便於用戶發現內存洩露,Netty提供4個檢測級別:

simple  以約1%的抽樣率檢測是否洩露,默認級別advanced  抽樣率同simple,但顯示詳細的洩露報告paranoid 抽樣率為100%,顯示報告信息同advanced

使用方法是在命令行參數設置:

-Dio.netty.leakDetectionLevel=[檢測級別]

示例程序如下,設置檢測級別為paranoid :


public static void main(String[] args) {
 for (int i = 0; i < 500000; ++i) {
 ByteBuf byteBuf = UnpooledByteBufAllocator.DEFAULT.buffer(1024);
   byteBuf = null;
 }
 System.gc();
}

可以看到控制臺輸出洩漏報告:

十二月 27, 2019 8:37:04 上午 io.netty.util.ResourceLeakDetector reportTracedLeak
嚴重: LEAK: ByteBuf.release() was not called before it's garbage-collected. See https://netty.io/wiki/reference-counted-objects.html for more information.
Recent access records:
Created at:
io.netty.buffer.UnpooledByteBufAllocator.newDirectBuffer(UnpooledByteBufAllocator.java:96)
io.netty.buffer.AbstractByteBufAllocator.directBuffer(AbstractByteBufAllocator.java:187)
io.netty.buffer.AbstractByteBufAllocator.directBuffer(AbstractByteBufAllocator.java:178)
io.netty.buffer.AbstractByteBufAllocator.buffer(AbstractByteBufAllocator.java:115)
org.caison.netty.demo.memory.BufferLeaksDemo.main(BufferLeaksDemo.java:15)

內存洩漏的原理是利用弱引用,弱引用(WeakReference)創建時需要指定引用隊列(refQueue),通過將ByteBuf對象用弱引用包裝起來(代碼入口位於AbstractByteBufAllocator#toLeakAwareBuffer()方法)

當發生GC時,如果GC線程檢測到ByteBuf對象只被弱引用對象關聯,會將該WeakReference加入refQueue

當ByteBuf內存被正常釋放,會調用WeakReference的clear()方法解除對ByteBuf的引用,後續GC線程不會再將該WeakReference加入refQueue;

Netty在每次創建ByteBuf時,基於抽樣率,抽樣命中時會輪詢(poll)refQueue中的WeakReference對象,輪詢返回的非null的WeakReference關聯的ByteBuf即為洩漏的堆外內存(具體代碼實現入口位於ResourceLeakDetector#track()方法)

3 圖形化工具

在代碼獲取堆外內存的基礎上,通過自定義接入一些監控工具定時檢測獲取,繪製圖形即可,例如比較流行的Prometheus或者Zabbix

也可以通過jdk自帶的Visualvm獲取,需要安裝Buffer Pools插件,底層原理是訪問MXBean中的監控指標,只能獲取hasCleaner的DirectByteBuffer的使用情況

此外,對於JNI調用產生的堆外內存分配,可以使用google-perftools進行監控

堆外內存洩漏診斷

堆外內存洩漏的具體原因比較多,先介紹任務隊列堆積的監控,再介紹通用堆外內存洩漏診斷思路

1 任務隊列堆積

這裡的任務隊列是值NioEventLoop中的QueuetaskQueue,提交到該任務隊列的場景有:

ctx.channel().eventLoop().execute(runnable);

channel.write(...)
channel.writeAndFlush(...)

ctx.channel().eventLoop().schedule(runnable, 60, TimeUnit.SECONDS);

當隊列中積壓任務過多,導致消息不能對對channel進行寫入然後進行釋放,會導致內存洩漏

診斷思路是對任務隊列中的任務數、積壓的ByteBuf大小、任務類信息進行監控,具體監控程序如下(代碼地址 https://github.com/caison/caison-blog-demo/tree/master/netty-demo):

public void channelActive(ChannelHandlerContext ctx) throws NoSuchFieldException, IllegalAccessException {
monitorPendingTaskCount(ctx);
 monitorQueueFirstTask(ctx);
 monitorOutboundBufSize(ctx);
}

public void monitorPendingTaskCount(ChannelHandlerContext ctx) {
 int totalPendingSize = 0;
 for (EventExecutor eventExecutor : ctx.executor().parent()) {
   SingleThreadEventExecutor executor = (SingleThreadEventExecutor) eventExecutor;
   
   
   totalPendingSize += executor.pendingTasks();
 }
 System.out.println("任務隊列中總任務數 = " + totalPendingSize);
}

public void monitorQueueFirstTask(ChannelHandlerContext ctx) throws NoSuchFieldException, IllegalAccessException {
 Field singleThreadField = SingleThreadEventExecutor.class.getDeclaredField("taskQueue");
 singleThreadField.setAccessible(true);
 for (EventExecutor eventExecutor : ctx.executor().parent()) {
   SingleThreadEventExecutor executor = (SingleThreadEventExecutor) eventExecutor;
   Runnable task = ((Queue<Runnable>) singleThreadField.get(executor)).peek();
   if (null != task) {
     System.out.println("任務隊列中第一個任務信息:" + task.getClass().getName());
   }
 }
}

public void monitorOutboundBufSize(ChannelHandlerContext ctx) {
 long outBoundBufSize = ((NioSocketChannel) ctx.channel()).unsafe().outboundBuffer().totalPendingWriteBytes();
 System.out.println("出站消息隊列中積壓的buf大小" + outBoundBufSize);
}

note: 上面程序至少需要基於Netty4.1.29版本才能使用,否則有性能問題

實際基於Netty進行業務開發,耗時的業務邏輯代碼應該如何處理?

先說結論,建議自定義一組新的業務線程池,將耗時業務提交業務線程池

Netty的worker線程(NioEventLoop),除了作為NIO線程處理連接數據讀取,執行pipeline上channelHandler邏輯,另外還有消費taskQueue中提交的任務,包括channel的write操作

如果將耗時任務提交到taskQueue,也會影響NIO線程的處理還有taskQueue中的任務,因此建議在單獨的業務線程池進行隔離處理

2 通用診斷思路

Netty堆外內存洩漏的原因多種多樣,例如代碼漏了寫調用release();通過retain()增加了ByteBuf的引用計數值而在調用release()時引用計數值未清空;因為Exception導致未能release();ByteBuf引用對象提前被GC,而關聯的堆外內存未能回收等等,這裡無法全部列舉,所以嘗試提供一套通用的診斷思路提供參考

首先,需要能復現問題,為了不影響線上服務的運行,儘量在測試環境或者本地環境進行模擬。但這些環境通常沒有線上那麼大的並發量,可以通過壓測工具來模擬請求

對於有些無法模擬的場景,可以通過Linux流量複製工具將線上真實的流量複製到到測試環境,同時不影響線上的業務,類似工具有Gor、tcpreplay、tcpcopy等

能復現之後,接下來就要定位問題所在,先通過前面介紹的監控手段、日誌信息試試能不能直接找到問題所在;如果找不到,就需要定位出堆外內存洩漏的觸發條件,但有時應用程式比較龐大,對外提供的流量入口很多,無法逐一排查。

在非線上環境的話,可以將流量入口注釋掉,每次注釋掉一半,然後再運行檢查問題還是否還存在,如果存在,繼續再注釋掉剩下的一半,通過這種二分法的策略通過幾次嘗試可以很快定位出觸發問題觸發條件

定位出觸發條件之後,再檢查程序中在該觸發條件處理邏輯,如果該處理程序很複雜,無法直接看出來,還可以繼續注釋掉部分代碼,二分法排查,直到最後找出具體的問題代碼塊

整套思路的核心在於,問題復現、監控、排除法,也可以用於排查其他問題,例如堆內內存洩漏、CPU 100%,服務進程掛掉等

總結

整篇文章側重於介紹知識點和理論,缺少實戰環節,這裡分享一些優質博客文章:

《netty 堆外內存洩露排查盛宴》 閃電俠手把手帶如何debug堆外內存洩漏

https://www.jianshu.com/p/4e96beb37935


《Netty防止內存洩漏措施》,Netty權威指南作者,華為李林峰內存洩漏知識分享 

https://mp.weixin.qq.com/s/IusIvjrth_bzvodhOMfMPQ


《疑案追蹤:Spring Boot內存洩露排查記》,美團技術團隊紀兵的案例分享

https://mp.weixin.qq.com/s/aYwIH0TN3nSzNaMR2FN0AA


《Netty入門與實戰:仿寫微信 IM 即時通訊系統》,閃電俠的掘金小冊(付費),個人就是學這個專欄入門Netty

https://juejin.im/book/5b4bc28bf265da0f60130116?referrer=598ff735f265da3e1c0f9643

END

Java面試題專欄




歡迎長按下圖關注公眾號後端技術精選

相關焦點

  • 一次完整的JVM堆外內存洩漏故障排查記錄
    前言記錄一次線上JVM堆外內存洩漏問題的排查過程與思路,其中夾帶一些「JVM內存分配的原理分析」以及「常用的JVM問題排查手段和工具分享」,希望對大家有所幫助。在整個排查過程中,我也走了不少彎路,但是在文章中我仍然會把完整的思路和想法寫出來,當做一次經驗教訓,給後人參考,文章最後也總結了下內存洩漏問題快速排查的幾個原則。
  • Java堆外內存排查小結
    簡介JVM堆外內存難排查但經常會出現問題,這可能是目前最全的JVM堆外內存排查思路。
  • Netty 內存模型分析(一)ByteBuf總覽
    本文開始,將主要圍繞netty相關知識展開,力求從宏觀上把握整個內存結構。
  • C語言內存洩漏,看完這篇就懂了!
    說明:預防內存洩漏問題有多種方法,如加強代碼檢視、工具檢測和內存測試等,本文聚集於開發人員能力提升方面。堆內存在C代碼中的存儲方式內存洩漏問題只有在使用堆內存的時候才會出現,棧內存不存在內存洩漏問題,因為棧內存會自動分配和釋放。
  • 獨家|Linux進程內存用量分析之堆內存篇
    而對於進程內存膨脹這類問題,原因通常有三個:1.內存洩漏。2.分配器管理的空閒內存較多而造成的內存空洞。3.有未統計使用的未知內存佔用。內存洩漏問題可以使用一些工具來檢測。但是對於後兩種問題,卻一直沒有比較通用的方法去確定。本文將介紹幾種內存洩漏檢測工具,並通過實際例子介紹一種分析堆內存佔用量的工具和方法,幫助定位內存膨脹問題。
  • ...既然妹紙不在家,剛好最近一直在為項目做內存洩漏的優化工作,那...
    反過來,如果圓節點與GC Roots不存在可達路徑,則意味著這塊對象的內存資源不再被程序引用,系統虛擬機可以在GC過程中將其回收掉。有了上面的內存回收的慄子,那麼接下來就可以說說什麼是內存洩漏了。從定義上講,Android(Java)平臺的內存洩漏是指沒有用的對象資源任與GC-Root保持可達路徑,導致系統無法進行回收。
  • Android 內存洩漏探討
    內存洩漏大家都不陌生了,簡單粗俗的講,就是該被釋放的對象沒有釋放,一直被某個或某些實例所持有卻不再被使用導致 GC 不能回收。最近自己閱讀了大量相關的文檔資料,打算做個 總結 沉澱下來跟大家一起分享和學習,也給自己一個警示,以後 coding 時怎麼避免這些情況,提高應用的體驗和質量。
  • 用valgrind定位內存洩漏
    結果,他對這個問題很陌生,感覺從來沒有思考過相關問題,也沒有做這方面的準備,自然就沒法正確作答,這讓我覺得有點吃驚。如果準備得不成功,那就要準備失敗了。在筆試面試中,遇到內存洩漏的定位問題,如果連valgrind都說不出來,那就很容易歇菜了。
  • 檢查是否內存洩漏的方法
    目錄前言一個簡單的內存洩漏檢查內存洩露的方法方法內存洩漏時,內存的消耗沒有內存洩露時的內存消耗前言這兩天抽空把之前買的這門課給看了看,課程很好,有興趣的也可以看一看:具體的課程總結過幾天寫一寫,這篇推送就說一下從這課上學到的一個技巧吧:一個檢查內存洩露的方法。
  • Android 輕鬆解決內存洩漏
    對於 C++ 來說,內存洩漏就是 new 出來的對象沒有 delete,俗稱野指針;而對於 java 而言,就是存放在堆上的 Object 無法被 GC 正常回收。的強引用就會導致內存洩漏。,造成內存洩漏。
  • 乾貨|Java 線上故障排查完整套路!牛掰!
    主要包括 OOM、GC 問題和堆外內存。一般來講,我們會先用free命令先來檢查一發內存的各種情況。堆內內存內存問題大多還都是堆內內存問題。表象上主要分為 OOM 和 Stack Overflo。)導入 dump 文件進行分析,內存洩漏問題一般我們直接選 Leak Suspects 即可,mat 給出了內存洩漏的建議。
  • 談談如何利用 valgrind 排查內存錯誤
    Memcheck 可以檢查哪些內存錯誤?Memcheck 可以檢查 c/c++ 程序中常見的以下問題:內存洩漏,包括進程運行過程中的洩漏和進程結束前的洩漏。不正確的釋放堆內存,比如 double free 或者 malloc/new/new[] 與 free/delete/delete[] 不匹配。
  • 一文探討堆外內存的監控與回收
    由這個線上問題,引出了這篇文章的主題,主要包括:FileChannel 源碼分析,堆外內存監控,堆外內存回收。大多數情況下,會申請一塊跟線程綁定的堆外緩存,這意味著,線程越多,這塊臨時的堆外緩存就越大。看到這兒,似乎線上的問題有了一點眉目:很有可能是多線程使用堆內內存寫入文件,而額外分配這塊堆外緩存導致了內存溢出。在驗證這個猜測之前,我們最好能直觀地監控到堆外內存的使用量,這才能增加我們定位問題的信心。
  • 「JVM教程與調優」了解JVM 堆內存溢出以及非堆內存溢出
    本文我們來介紹一下jmap+MAT內存溢出。首先我們來介紹一下下JVM的內存結構。JVM內存結構介紹從圖中我們可以看到,JVM的內存結構分為兩大塊。一塊叫堆區,一塊叫非堆區。這裡面@Data、@AllArgsConstructor、@NoArgsConstructor用的是lombok註解。不會使用的小夥伴,可以在網上查找相關資料學習一下。
  • Java線上故障排查必備指南
    內存問題排查起來相對比 CPU 麻煩一些,場景也比較多。主要包括 OOM、GC 問題和堆外內存。一般來講,我們會先用free命令先來檢查一發內存的各種情況。堆外內存如果碰到堆外內存溢出,那可真是太不幸了。
  • 【221期】面試官:談談內存洩漏和內存溢出的聯繫與區別
    (memory leak)1、內存洩漏是指程序中已動態分配的堆內存由於某種原因未釋放或無法釋放,造成系統內存的浪費,導致程序運行速度減慢甚至系統奔潰等嚴重後果。2、一次內訓洩漏似乎不會有大的影響,但內存洩漏後堆積的結果就是內存溢出。3、內存洩漏具有隱蔽性,積累性的特徵,比其他內存非法訪問錯誤更難檢測。這是因為內存洩漏產生的原因是內存塊未被釋放,屬於遺漏型缺陷而不是過錯型缺陷。
  • Android 性能優化之內存洩漏,使用MAT&LeakCanary解決問題
    ,內存這個小妮子比較調皮,每個月總有那麼幾次洩漏或者溢出(OOM),這篇文章所講的是內存溢出,這裡要注意,內存溢出和內存洩漏是兩個概念,這點大家要清楚,當然,內存洩漏過多會導致內存洩漏,至於什麼是內存洩漏呢,大家都知道我們的內存回收機制是GC,所以用一句話來概括:GC回收機制所無法回收的垃圾對象。
  • JavaScript內存管理機制以及四種常見的內存洩漏解析
    而這第三篇文章將討論另一個很重要的主題——內存管理。隨著程式語言變得越來越成熟越來越複雜,開發人員很容易忽視這一問題。同時,本文還將提供一些處理JavaScript內存洩漏的技巧,既能確保SessionStack不會出現內存洩漏,也不會增加web應用程式的內存佔用。
  • 理解閉包與內存洩漏
    三、內存洩漏內存洩漏常常與閉包緊緊聯繫在一起,很容易讓人誤以為閉包就會導致內存洩漏。其實閉包只是讓內存常駐,而濫用閉包才會導致內存洩漏。內存洩漏,從廣義上說就是,內存在使用完畢之後,對於不再要的內存沒有及時釋放或者無法釋放。不再需要的內存使用完畢之後肯定需要釋放掉,否則這個塊內存就浪費掉了,相當於內存洩漏了。但是在實際中,往往不會通過判斷該內存或變量是否不再需要使用來判斷。因為內存測試工具很難判斷該內存是否不再需要。