標籤:java 虛擬機器 記憶體最佳化 c++
上一篇我們講述了Java虛擬機器的體繫結構和記憶體模型,那麼我們就不得不說到記憶體泄露。大家都知道,Java是從C++的基礎上發展而來的,而C++程式的很大的一個問題就是記憶體泄露難以解決,儘管Java的JVM有一套自己的記憶體回收機制來回收記憶體,在大多數的情況下並不需要java程式開發人員操太多的心,但也是存在泄露問題的,只是比C++小一點。比如說,程式中存在被引用但無用的對象:程式引用了該對象,但後續不會或者不能再使用它,那麼它佔用的記憶體空間就浪費了。
我們先來看看GC是如何工作的:監控每一個對象的運行狀態,包括對象的申請、引用、被引用、賦值等,當該對象不再被引用時,釋放對象(GC本文的重點,不做過多闡述)。很多Java程式員過分依賴GC,但問題的關鍵是無論JVM的記憶體回收機製做得多好,記憶體總歸是有限的資源,因此就算GC會為我們完成了大部分的記憶體回收,但適當地注意編碼過程中的記憶體最佳化還是很必要的。這樣可以有效減少GC次數,同時提升記憶體利用率,最大限度地提高程式的效率。
總體而言,Java虛擬機器的記憶體最佳化應從兩方面著手:Java虛擬機器和Java應用程式。前者指根據應用程式的設計通過虛擬機器參數控制虛擬機器邏輯記憶體分區的大小以使虛擬機器的記憶體與程式對記憶體的需求相得益彰;後者指最佳化程式演算法,降低GC負擔,提高GC回收成功率。
通過參數最佳化虛擬機器記憶體的參數如下所示:
-Xms
初始Heap大小
-Xmx
java heap最大值
-Xmn
young generation的heap大小
-Xss
每個線程的Stack大小
上面是三個比較常用的參數,還有一些:
-XX:MinHeapFreeRatio=40
Minimum percentage of heap free after GC to avoid expansion.
-XX:MaxHeapFreeRatio=70
Maximum percentage of heap free after GC to avoid shrinking.
-XX:NewRatio=2
Ratio of new/old generation sizes. [Sparc -client:8; x86 -server:8; x86 -client:12.]-client:8 (1.3.1+), x86:12]
-XX:NewSize=2.125m
Default size of new generation (in bytes) [5.0 and newer: 64 bit VMs are scaled 30% larger; x86:1m; x86, 5.0 and older: 640k]
-XX:MaxNewSize=
Maximum size of new generation (in bytes). Since 1.4, MaxNewSize is computed as a function of NewRatio.
-XX:SurvivorRatio=25
Ratio of eden/survivor space size [Solaris amd64: 6; Sparc in 1.3.1: 25; other Solaris platforms in 5.0 and earlier: 32]
-XX:PermSize=
Initial size of permanent generation
-XX:MaxPermSize=64m
Size of the Permanent Generation. [5.0 and newer: 64 bit VMs are scaled 30% larger; 1.4 amd64: 96m; 1.3.1 -client: 32m.]
下面所說通過最佳化程式演算法來提高記憶體利用率,並降低記憶體風險,完全是經驗之談,僅供參考,謝謝!
1.儘早釋放無用對象的引用(XX = null;)
看一段代碼:
public List<PageData> parse(HtmlPage page) {
List<PageData> list = null;
try {
List valueList = page.getByXPath(config.getContentXpath());
if (valueList == null || valueList.isEmpty()) {
return list;
}
//需要時才建立對象,節省記憶體,提高效率
list = new ArrayList<PageData>();
PageData pageData = new PageData();
StringBuilder value = new StringBuilder();
for (int i = 0; i < valueList.size(); i++) {
HtmlElement content = (HtmlElement) valueList.get(i);
DomNodeList<HtmlElement> imgs = content.getElementsByTagName("img");
if (imgs != null && !imgs.isEmpty()) {
for (HtmlElement img : imgs) {
try {
HtmlImage image = (HtmlImage) img;
String path = image.getSrcAttribute();
String format = path.substring(path.lastIndexOf("."), path.length());
String localPath = "D:/images/" + MD5Helper.md5(path).replace("\\", ",").replace("/", ",") + format;
File localFile = new File(localPath);
if (!localFile.exists()) {
localFile.createNewFile();
image.saveAs(localFile);
}
image.setAttribute("src", "file:///" + localPath);
localFile = null;
image = null;
img = null;
} catch (Exception e) {
}
}
//這個對象以後不會在使用了,清除對其的引用,等同於提前告知GC,該對象可以回收了
imgs = null;
}
String text = content.asXml();
value.append(text).append("<br/>");
valueList=null;
content = null;
text = null;
}
pageData.setContent(value.toString());
pageData.setCharset(page.getPageEncoding());
list.add(pageData);
//這裡 pageData=null; 是沒用的,因為list仍然持有該對象的引用,GC不會回收它
value=null;
//這裡可不能 list=null; 因為list是方法的傳回值,否則你從該方法中得到的傳回值永遠為空白,而且這種錯誤不易被發現、排除
} catch (Exception e) {
}
return list;
}
PS:如有任何問題,請直接在群457036818中提出。
本文出自 “JAVA學習視頻教程” 部落格,請務必保留此出處http://10239772.blog.51cto.com/10229772/1659199
探討深入Java虛擬機器之記憶體最佳化