国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發(fā)設計 > 正文

處理Java程序中的內(nèi)存漏洞

2019-11-18 13:11:14
字體:
來源:轉載
供稿:網(wǎng)友

  java 程序中也有內(nèi)存漏洞?當然有。與流行的觀念相反,在 Java 編程中,內(nèi)存治理仍然是需要考慮的問題。在本文中,您將了解到什么會導致內(nèi)存漏洞以及何時應該關注這些漏洞。您還有機會實踐一下在您自己的項目中解決漏洞問題。
  
  Java 程序中的內(nèi)存漏洞是如何顯現(xiàn)出來的
  大多數(shù)程序員都知道,使用像 Java 這樣的編程語言的一大好處就是,他們不必再擔心內(nèi)存的分配和釋放問題。您只須創(chuàng)建對象,當應用程序不再需要這些對象時,Java 會通過一種稱為“垃圾收集”的機制將這些對象刪除。這種處理意味著 Java 已經(jīng)解決了困擾其他編程語言的煩人問題 -- 可怕的內(nèi)存漏洞。是這樣的嗎?
  
  在深入討論之前,我們先回顧一下垃圾收集的工作方式。垃圾收集器的工作是發(fā)現(xiàn)應用程序不再需要的對象,并在這些對象不再被訪問或引用時將它們刪除。垃圾收集器從根節(jié)點(在 Java 應用程序的整個生存周期內(nèi)始終存在的那些類)開始,遍歷被引用的所有節(jié)點進行清除。在它遍歷這些節(jié)點的同時,它跟蹤哪些對象當前正被引用著。任何類只要不再被引用,它就符合垃圾收集的條件。當刪除這些對象以后,就可將它們所占用的內(nèi)存資源返回給 Java 虛擬機 (JVM)。
  
  所以的確是這樣,Java 代碼不要求程序員負責內(nèi)存的治理和清除,它會自動對無用的對象執(zhí)行垃圾收集。但是,要緊記的一點是 僅當一個對象不再被引用時才會被統(tǒng)計為無用的。圖 1 說明了這個概念。
  
 處理Java程序中的內(nèi)存漏洞(圖一)

  
圖 1. 無用但仍被引用的對象

  
  上面說明了在 Java 應用程序執(zhí)行期間具有不同生存周期的兩個類。類 A 首先被實例化,并會在很長一段時間或程序的整個生存期內(nèi)存在。在某個時候,類 B 被創(chuàng)建,類 A 添加對這個新創(chuàng)建的類的一個引用。現(xiàn)在,我們假定類 B 是某個用戶界面小部件,它由用戶顯示甚至解除。假如沒有清除類 A 對 B 的引用,則即便不再需要類 B,并且即便在執(zhí)行下一個垃圾收集周期以后,類 B 仍將存在并占用內(nèi)存空間。
  
  何時應該關注內(nèi)存漏洞?
  假如您的程序在執(zhí)行一段時間以后發(fā)出 java.lang.OutOfMemoryError 錯誤,則內(nèi)存漏洞肯定是一個重大嫌疑。除了這種明顯的情況之外,何時還應該關注內(nèi)存漏洞呢?持完美主義觀點的程序員肯定會回答,應該查找并糾正 所有內(nèi)存漏洞。但是,在得出這個結論之前,還有幾個方面需要考慮,包括程序的生存期和漏洞的大小。
  
  完全有這樣的可能,垃圾收集器在應用程序的生存期內(nèi)可能始終不會運行。不能保證 JVM 何時以及是否會調(diào)用垃圾收集器 -- 即便程序顯式地調(diào)用 System.gc() 也是如此。通常,在當前的可用內(nèi)存能夠滿足程序的內(nèi)存需求時,JVM 不會自動運行垃圾收集器。當可用內(nèi)存不能滿足需求時,JVM 將首先嘗試通過調(diào)用垃圾收集來釋放出更多的可用內(nèi)存。假如這種嘗試仍然不能釋放足夠的資源,JVM 將從操作系統(tǒng)獲取更多的內(nèi)存,直至達到答應的最大極限。
  
  例如,考慮一個小型 Java 應用程序,它顯示一些用于修改配置的簡單用戶界面元素,并且它有一個內(nèi)存漏洞。很可能到應用程序關閉時也不會調(diào)用垃圾收集器,因為 JVM 很可能有足夠的內(nèi)存來創(chuàng)建程序所需的全部對象,而此后可用內(nèi)存則所剩無幾。因此,在這種情況下,即使某些“死”對象在程序執(zhí)行時占用著內(nèi)存,它實際上并沒有什么用途。
  
  假如正在開發(fā)的 Java 代碼要全天 24 小時在服務器上運行,則內(nèi)存漏洞在此處的影響就比在我們的配置實用程序中的影響要大得多。在要長時間運行的某些代碼中,即使最小的漏洞也會導致 JVM 耗盡全部可用內(nèi)存。
  
  在相反的情況下,即便程序的生存期較短,假如存在分配大量臨時對象(或者若干吞噬大量內(nèi)存的對象)的任何 Java 代碼,而且當不再需要這些對象時也沒有取消對它們的引用,則仍然可能達到內(nèi)存極限。
  
  最后一種情況是內(nèi)存漏洞無關緊要。我們不應該認為 Java 內(nèi)存漏洞像其他語言(如 C++)中的漏洞那樣危險,在那些語言中內(nèi)存將丟失,且永遠不會被返回給操作系統(tǒng)。在 Java 應用程序中,我們使不需要的對象依附于操作系統(tǒng)為 JVM 所提供的內(nèi)存資源。所以從理論上講,一旦關閉 Java 應用程序及其 JVM,所分配的全部內(nèi)存將被返回給操作系統(tǒng)。
  
  確定應用程序是否有內(nèi)存漏洞
  為了查看在 Windows NT 平臺上運行的某個 Java 應用程序是否有內(nèi)存漏洞,您可能試圖在應用程序運行時觀察“任務治理器”中的內(nèi)存設置。但是,在觀察了運行中的幾個 Java 應用程序以后,您會發(fā)現(xiàn)它們比本地應用程序占用的內(nèi)存要多得多。我做過的一些 Java 項目要使用 10 到 20 MB 的系統(tǒng)內(nèi)存才能啟動。而操作系統(tǒng)自帶的 Windows EXPlorer 程序只需 5 MB 左右的內(nèi)存。
  
  在 Java 應用程序內(nèi)存使用方面應注重的另一點是,這個典型程序在 IBM JDK 1.1.8 JVM 中運行時占用的系統(tǒng)內(nèi)存越來越多。似乎直到為它分配非常多的物理內(nèi)存以后它才開始向系統(tǒng)返回內(nèi)存。這些情況是內(nèi)存漏洞的征兆嗎?
  
  要理解其中的緣由,我們必須熟悉 JVM 如何將系統(tǒng)內(nèi)存用作它的堆。當運行 java.exe 時,您使用一定的選項來控制垃圾收集堆的起始大小和最大大小(分別用 -ms 和 -mx 表示)。Sun JDK 1.1.8 的默認起始設置為 1 MB,默認最大設置為 16 MB。IBM JDK 1.1.8 的默認最大設置為系統(tǒng)總物理內(nèi)存大小的一半。這些內(nèi)存設置對 JVM 在用盡內(nèi)存時所執(zhí)行的操作有直接影響。JVM 可能繼續(xù)增大堆,而不等待一個垃圾收集周期的完成。
  
  這樣,為了查找并最終消除內(nèi)存漏洞,我們需要使用比任務監(jiān)視實用程序更好的工具。當您試圖調(diào)試內(nèi)存漏洞時,內(nèi)存調(diào)試程序(請參閱參考資源)可能派得上用場。這些程序通常會顯示堆中的對象數(shù)、每個對象的實例數(shù)和這些對象所占用的內(nèi)存等信息。此外,它們也可能提供有用的視圖,這些視圖可以顯示每個對象的引用和引用者,以便您跟蹤內(nèi)存漏洞的來源。
  
  下面我將說明我是如何用 Sitraka Software 的 JPRobedebugger 檢測和去除內(nèi)存漏洞的,以使您對這些工具的部署方式以及成功去除漏洞所需的過程有所了解。
  
  內(nèi)存漏洞的一個示例
  本例集中討論一個問題,我們部門當時正在開發(fā)一個商業(yè)發(fā)行版軟件,這是一個 Java JDK 1.1.8 應用程序,一個測試人員花了幾個小時研究這個程序才最終使這個問題顯現(xiàn)出來。這個 Java 應用程序的基本代碼和包是由幾個不同的開發(fā)小組在不同的時間開發(fā)的。我猜想,該應用程序中意外出現(xiàn)的內(nèi)存漏洞是由那些沒有真正理解別人開發(fā)的代碼的程序員造成的。
  
  我們正在討論的 Java 代碼答應用戶為 Palm 個人數(shù)字助理創(chuàng)建應用程序,而不必編寫任何 Palm OS 本地代碼。通過使用圖形用戶界面,用戶可以創(chuàng)建窗體,向窗體中添加控件,然后連接這些控件的事件來創(chuàng)建 Palm 應用程序。測試人員發(fā)現(xiàn),隨著不斷創(chuàng)建和刪除窗體和控件,這個 Java 應用程序最終會耗盡內(nèi)存。開發(fā)人員沒有檢測到這個問題,因為他們的機器有更多的物理內(nèi)存。
  
  為了研究這個問題,我用 JProbe 來確定什么地方出了差錯。盡管用了 JProbe 所提供的強大工具和內(nèi)存快照,研究仍然是一個冗長乏味、不斷重復的過程,首先要確定出現(xiàn)內(nèi)存漏洞的原因,然后修改代碼,最后還得檢驗結果。
  
  JProbe 提供幾個選項,用來控制調(diào)試期間實際記錄哪些信息。經(jīng)過幾次試驗以后,我斷定獲取所需信息的最有效方法是,關閉性能數(shù)據(jù)收集,而將注重力集中在所捕捉的堆數(shù)據(jù)上。JProbe 提供了一個稱為 Runtime Heap Summary 的視圖,它顯示 Java 應用程序運行時所占用的堆內(nèi)存量隨時間的變化。它還提供了一個工具欄按鈕,必要時可以強制 JVM 執(zhí)行垃圾收集。假如您試圖弄清楚,當 Java 應用程序不再需要給定的類實例時,這個實例會不會被作為垃圾收集,這個功能將很有用。圖 2 顯示了使用中的堆存儲量隨時間的變化。
  
 處理Java程序中的內(nèi)存漏洞(圖二)

  
圖 2. Runtime Heap Summary

  
  在 Heap Usage Chart 中,藍色部分表明已分配的堆空間大小。在啟動這個 Java 程序并達到穩(wěn)定狀態(tài)以后,我強制垃圾收集器運行,在圖中的表現(xiàn)就是綠線(這條線表明插入了一個檢查點)左側的藍線的驟降。隨后,我添加了四個窗體,然后又將它們刪除,并再次調(diào)用了垃圾收集器。當程序返回僅有一個可視窗體的初始狀態(tài)時,檢查點之后的藍色區(qū)域高于檢查點之前的藍色區(qū)域這一情況表明可能存在內(nèi)存漏洞。我通過查看 Instance Summary 證實確實有一個漏洞,因為 Instance Summary 表明 FormFrame 類(它是窗體的主用戶界面類)的計數(shù)在檢查點之后增加了 4。
  
  查找原因
  為了將測試人員報告的問題剔出,我采取的第一個步驟是找出幾個簡單的、可重復的測試案例。就本例而言,我發(fā)現(xiàn)只須添加一個窗體,將它刪除,然后強制執(zhí)行垃圾收集,結果就會導致與被刪除窗體相關聯(lián)的許多類實例仍然處于活動狀態(tài)。這個問題在 JProbe 的 Instance Summary 視圖中很明顯,這個視圖統(tǒng)計每個 Java 類在堆中的實例數(shù)。
  
  為了查明使垃圾收集器無法正常完成其工作的那些引用,我使用 JProbe 的 Reference Graph(如圖 3 所示)來確定哪些類仍然引用著目前未被刪除的 FormFrame 類。在調(diào)試這個問題時該過程是最復雜的過程之一,因為我發(fā)現(xiàn)許多不同的對象仍然引用著這個無用的對象。用來查明究竟是哪個引用者真正造成這個問題的試錯過程相當耗時。
  
  在本例中,一個根類(左上角用紅色標明的那個類)是問題的發(fā)源地。右側用藍色突出顯示的類處在從最初的 FormFrame 類跟蹤而來的路徑上。
  
處理Java程序中的內(nèi)存漏洞(圖三)

  
圖 3. 在引

發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 肃宁县| 句容市| 阿鲁科尔沁旗| 滦平县| 嘉祥县| 龙泉市| 克拉玛依市| 乌兰浩特市| 盱眙县| 大石桥市| 来宾市| 社旗县| 尼木县| 灵石县| 津市市| 二连浩特市| 武宣县| 五台县| 宁强县| 龙门县| 泉州市| 班玛县| 宁强县| 舒兰市| 诸城市| 从化市| 镶黄旗| 河北区| 沈丘县| 上思县| 游戏| 金乡县| 岳西县| 德保县| 沭阳县| 蓬安县| 和顺县| 瓦房店市| 若尔盖县| 通州市| 聊城市|