java內(nèi)存泄露排查總結(jié)
點擊上方藍色字體,選擇“標(biāo)星公眾號”
優(yōu)質(zhì)文章,第一時間送達
作者 | 那個少年~
來源 | urlify.cn/FvQVzi
1.內(nèi)存溢出和內(nèi)存泄露
內(nèi)存溢出:你申請了10個字節(jié)的空間,但是你在這個空間寫入了11個或者以上字節(jié)的數(shù)據(jù),則出現(xiàn)溢出
內(nèi)存泄露:你用new申請了一塊內(nèi)存,后來很長時間都不使用了,但是因為一直被某個或者某些實例所持有導(dǎo)致GC不能回收掉,也就是該釋放的對象沒有釋放,則出現(xiàn)泄露。
1.1 內(nèi)存溢出
JVM內(nèi)存過小
程序不嚴(yán)密,產(chǎn)生了過多的垃圾
內(nèi)存中加載的數(shù)據(jù)量過大,如一次性從數(shù)據(jù)庫取出過多數(shù)據(jù)
集合類中有對對象的引用,使用完后沒有清空,是jvm不能回收
代碼中存在死循環(huán)或循環(huán)中產(chǎn)生過多重復(fù)的對象實體
使用第三方軟件中的bug
啟動參數(shù)內(nèi)存值設(shè)定過小
tomcat:java.lang.OutOfMemoryError: PermGen space
tomcat:java.lang.OutOfMemoryError: Java heap space
weblogic:Root cause of ServletException java.lang.OutOfMemoryError
resin:java.lang.OutOfMemoryError
java:java.lang.OutOfMemoryError
增加JVM的內(nèi)存大小:對于tomcat容器,找到tomcat在電腦中的安裝目錄,進入這個目錄,然后進入bin目錄中,在window環(huán)境下找到bin目錄中的catalina.bat,在linux環(huán)境下找到catalina.sh。編輯catalina.bat文件,找到JAVA_OPTS(具體來說是
set "JAVA_OPTS=%JAVA_OPTS% %LOGGING_MANAGER%")這個選項的位置,這個參數(shù)是Java啟動的時候,需要的啟動參數(shù)。也可以在操作系統(tǒng)的環(huán)境變量中對JAVA_OPTS進行設(shè)置,因為tomcat在啟動的時候,也會讀取操作系統(tǒng)中的環(huán)境變量的值,進行加載。如果是修改了操作系統(tǒng)的環(huán)境變量,需要重啟機器,再重啟tomcat,如果修改的是tomcat配置文件,需要將配置文件保存,然后重啟tomcat,設(shè)置就能生效了。優(yōu)化程序,釋放垃圾:主要思路就是避免程序體現(xiàn)上出現(xiàn)的情況。避免死循環(huán),防止一次載入太多的數(shù)據(jù),提高程序健壯型及時釋放。因此,從根本上解決Java內(nèi)存溢出的唯一方法就是修改程序,及時地釋放沒用的對象,釋放內(nèi)存空間。
1.2 內(nèi)存泄露
首先,這些對象是可達的,即在有向圖中,存在通路可以與其相連;
其次,這些對象是無用的,即程序以后不會再使用這些對象。
1.3 內(nèi)存溢出和內(nèi)存泄露的聯(lián)系
2、一個Java內(nèi)存泄漏的排查案例
2.1 確定頻繁的Full GC現(xiàn)象
2.2 找出頻繁Full GC的原因
把堆dump下來在用MAT等工具進行分析,但是dump堆要花較長時間,并且文件巨大,再從服務(wù)器上拖回本地導(dǎo)入工具,這個過程有些折騰,不到萬不得已最好別這么干。
更輕量級的在線分析,使用jmap(java內(nèi)存影響工具)生成堆轉(zhuǎn)存快照(一般稱為headdump或者dump文件)
2.3 定位到代碼
用工具生成java應(yīng)用程序的heap dump(如jmap)
使用Java heap分析工具(如MAT),找出內(nèi)存占用超出預(yù)期的嫌疑對象
根據(jù)情況,分析嫌疑對象和其他對象的引用關(guān)系。
分析程序的源代碼,找出嫌疑對象數(shù)量過多的原因。
ps -ef|grep java
jmap -histo:live 進程號 | head -7
jmap -dump:live,format=b,file=heap.hprof 3514
安裝MAT插件
在eclipse里切換到Memory Analysis視圖
用MAT打開heap profile文件。
public class RefreshCmsOrganizationStruts implements Runnable{
private final static Logger logger = Logger.getLogger(RefreshCmsOrganizationStruts.class);
private List<Cms_Organization> organizations;
private OrganizationDao organizationDao = (OrganizationDao) WebContentBean
.getInstance().getBean("organizationDao");
public RefreshCmsOrganizationStruts(List<Cms_Organization> organizations) {
this.organizations = organizations;
}
public void run() {
Iterator<Cms_Organization> iter = organizations.iterator();
Cms_Organization organization = null;
while (iter.hasNext()) {
organization = iter.next();
synchronized (organization) {
try {
organizationDao.refreshCmsOrganizationStrutsInfo(organization.getOrgaId());
organizationDao.refreshCmsOrganizationResourceInfo(organization.getOrgaId());
organizationDao.sleep();
} catch (Exception e) {
logger.debug("RefreshCmsOrganizationStruts organization = " + organization.getOrgaId(), e);
}
}
}
}
}
public class CategoryCacheJob extends QuartzJobBean implements StatefulJob {
private static final Logger LOGGER = Logger.getLogger(CategoryCacheJob.class);
public static Map<String,List<Cms_Category>> cacheMap = new java.util.HashMap<String,List<Cms_Category>>();
@Override
protected void executeInternal(JobExecutionContext ctx) throws JobExecutionException {
try {
//LOGGER.info("======= 緩存編目樹開始 =======");
MongoBaseDao mongoBaseDao = (MongoBaseDao) BeanLocator.getInstance().getBean("mongoBaseDao");
MongoOperations mongoOperations = mongoBaseDao.getMongoOperations();
/*
LOGGER.info("1.緩存基礎(chǔ)教育編目樹");
Query query = Query.query(Criteria.where("isDel").is("0").and("categoryType").is("F"));
query.sort().on("orderNo", Order.ASCENDING);
List<Cms_Category> list = mongoOperations.find(query, Cms_Category.class);
String key = query.toString().replaceAll("\\{|\\}|\\p{Cntrl}|\\p{Space}", "");
key += "_CategoryCacheJob";
cacheMap.put(key, list);
*/
//LOGGER.info("2.緩存職業(yè)教育編目樹");
Query query2 = Query.query(Criteria.where("isDel").is("0").and("categoryType").in("JMP","JHP"));
query2.sort().on("orderNo", Order.ASCENDING);
List<Cms_Category> list2 = mongoOperations.find(query2, Cms_Category.class);
String key2 = query2.toString().replaceAll("\\{|\\}|\\p{Cntrl}|\\p{Space}", "");
key2 += "_CategoryCacheJob";
cacheMap.put(key2, list2);
//LOGGER.info("3.緩存專題教育編目樹");
Query query3 = Query.query(Criteria.where("isDel").is("0").and("categoryType").is("JS"));
query3.sort().on("orderNo", Order.ASCENDING);
List<Cms_Category> list3 = mongoOperations.find(query3, Cms_Category.class);
String key3 = query3.toString().replaceAll("\\{|\\}|\\p{Cntrl}|\\p{Space}", "");
key3 += "_CategoryCacheJob";
cacheMap.put(key3, list3);
//LOGGER.info("======= 緩存編目樹結(jié)束 =======");
} catch(Exception ex) {
LOGGER.error(ex.getMessage(), ex);
LOGGER.info("======= 緩存編目樹出錯 =======");
}
}
}
public boolean add(E e) {
//1、先加鎖
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
//2、拷貝數(shù)組
Object[] newElements = Arrays.copyOf(elements, len + 1);
//3、將元素加入到新數(shù)組中
newElements[len] = e;
//4、將array引用指向到新數(shù)組
setArray(newElements);
return true;
} finally {
//5、解鎖
lock.unlock();
}
}
由于寫操作的時候,需要拷貝數(shù)組,會消耗內(nèi)存,如果原數(shù)組的內(nèi)容比較多的情況下,可能導(dǎo)致yong gc或者full gc
不能用于實時讀的場景,像拷貝數(shù)組,新增元素都需要時間,所以調(diào)用一個set操作后,讀取到的數(shù)據(jù)可能還是舊的,雖然
CopyOnWriteArrayList能做到最終一致性,但是還無法滿足實時性的要求
鋒哥最新SpringCloud分布式電商秒殺課程發(fā)布
??????
??長按上方微信二維碼 2 秒
感謝點贊支持下哈 











