jvm工具看内存泄露

实验的示例代码附在最后(jvisualvm , Jconsole- jdk8)

内存泄露通常表现为,使用的内存不断增长。

如果看到的类实例数,非java包类排在前10,就可能会有问题。结合jdk工具对长时间运行的j应用程序监控得到的数字,进行分析。


实验vm 参数:

-Xms3072M -Xmx3072M  -Xmn512M -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=50 -XX:+PrintGCDetails -Xloggc:F:/mysql-5.7.20-winx64/gc.log

直观的看图:

1.不正常的图:

总堆内存趋势



CMS Old Gen 区域占用内存一直缓慢升高




PreparedStatement这个类的实例很多,所以,需要在程序中查找产生这个类例的代码,看有没有问题。


2.正常的图:





实验的关键代码是这段:(preStmt不关闭

finally{  //这段代码是区分内存正常与否的一段,去掉,内存就会缓慢增长,直至OOM
try {
preStmt.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}


另外:

去掉这个代码:

sb=null;//去掉内存占用情况会是锯齿状
System.gc();//去掉内存占用情况会是锯齿状

内存占用情况会是锯齿状






不调用GC代码:

sb=null;
//System.gc();//去掉内存占用情况会是锯齿状





调用GC代码:

sb=null;
System.gc();//去掉内存占用情况会是锯齿状






内存多少合适?

参数说明

-XX:SurvivorRatio:  eden/单个survivor的比值,  -XX:SurvivorRatio =1,eden大小和单个Survivor一样;-XX:SurvivorRatio =2,eden大小是单个Survivor的两倍大;-XX:SurvivorRatio =3,eden大小是单个Survivor的三倍大

-XX:CMSInitiatingOccupancyFraction=50:oldGen占用内存达到50%,cms起来干活,早点回做垃圾回收的动作。


理论上,对象的内存,在eden和survivor区域被回收是最好的情况,survivor的大小理论上应该和eden回收后活下来的对象差不多大,oldGen的大小应该差不多就是多次survivor,仍然活下来的对象差不多大。
根据上面的情况:eden区域的对象,大致在60M内,survivor应该也差不多在60以下,而oldGen里的对象基本稳定在15M左右。
所以这个程序应该要分配的内存,大致为:60(eden)+60*2(两个survivor)+15=200M左右内存。

jvm启动参数设置如下:
-Xms256M -Xmx256M  -Xmn200M -XX:SurvivorRatio=2 -XX:MaxTenuringThreshold=3 -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=50 -XX:+PrintGCDetails -Xloggc:F:/mysql-5.7.20-winx64/gc.log

再看内存:




实际eden区在20M以内,oldGen在12M,而survivor基本没有使用。

再调参数:

20(eden)+1*2(两个survivor,不经过survivor)+15=40M左右内存。


-Xms40M -Xmx40M  -Xmn25M -XX:SurvivorRatio=10 -XX:MaxTenuringThreshold=3 -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=50 -XX:+PrintGCDetails -Xloggc:F:/mysql-5.7.20-winx64/gc.log

看内存:





效果基本符合预期,oldGen占用比存比有点高,看着有点悬,可以适当加大一倍。



//实验示例代码(读取一个25G的数据文件,放到mysql数据库)

package com.ape.util.file;


import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;


public class Demo {


public static final String url = "jdbc:mysql://127.0.0.1/atii";
public static final String name = "com.mysql.jdbc.Driver";
public static final String user = "atii";
public static final String password = "123456";
public static Connection conn = null;
public static PreparedStatement preStmt = null;
static {
try {
if (conn == null) {
Class.forName(name);// 指定连接类型
conn = DriverManager.getConnection(url, user, password);// 获取连接
}


} catch (Exception e) {
e.printStackTrace();
}
}


static String sql = null;
static ResultSet ret = null;
static int lineNum = 0;
static int batchCount = 0;


public static void main(String[] args) {


// queryCount();
// deleteSql();
// querySql();//100791444
insertFromFile(10000000, 100);


}


public static void querySql() {
sql = "select * from t_at_ticketorder limit 0,10";// SQL语句
try {
preStmt = conn.prepareStatement(sql);// 准备执行语句
ret = preStmt.executeQuery(sql);// 执行语句,得到结果集
while (ret.next()) {
String uid = ret.getString(1);
String ufname = ret.getString(2);
String ulname = ret.getString(3);
String udate = ret.getString(4);
System.out.println(uid + "\t" + ufname + "\t" + ulname + "\t" + udate);
} // 显示数据
ret.close();
} catch (SQLException e) {
e.printStackTrace();
}
}


public static void deleteSql() {
sql = "TRUNCATE TABLE t_at_ticketorder";// SQL语句 TRUNCATE TABLE 表名
try {
preStmt = conn.prepareStatement(sql);// 准备执行语句
preStmt.executeUpdate(sql);// 执行语句,得到结果集
// db1.close();// 关闭连接
} catch (SQLException e) {
e.printStackTrace();
}
}


public static void insertSql(String sql) {
System.out.println(batchCount++ +" :"+ sql.length());
if(batchCount%10==0){
try {
Thread.currentThread().sleep(50);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
long starttime = System.currentTimeMillis();
try {
preStmt = conn.prepareStatement(sql);// 准备执行语句
preStmt.executeUpdate(sql);// 执行语句,得到结果集
// db1.close();// 关闭连接
} catch (SQLException e) {
System.out.println(sql);
System.out.println(lineNum);
writeToFile(sql);
e.printStackTrace();
}finally{  //这段代码是区分内存正常与否的一段,去掉,内存就会缓慢增长,直至OOM
try {
preStmt.close();
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

long cost = (System.currentTimeMillis() - starttime) / 1000;
System.out.println("cost:" + cost);
try {
Thread.currentThread().sleep(50);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}


public static void insertFromFile(int countNum, int perBatchNum) {
String head = "insert into t_at_ticketorder (ID, ORDERCD, MEMBERCD, STATUS, OPERATERID, CREATETIME, TASKID, ORDERSOURCE, CREATOR, CUSTREQUEST, FOREREMARK, MIDAUDIT, CANTREASON, CANTDESC, ATTACHORDERID, URGENCE, ISSUEDAY, ORDERTYPE, AUDITOR, AUDITTIME, INVOICEFLAG, PRINTINSFLAG, URGEMESSAGE, OPERATETIME, CONFIRMSTATUS, TKTLTIME, MEMBERNAME, MEMBERTYPE, HANDFLAG, OPENFLAG, BUYFLAG, LOGID, LOGTIME, DELIVERYINFOREMARK, VIPLEVEL, XO, DAXIA, SENDFAXTIME, HANDLECOUNT, ERRORFLAG, CLIENTCD, CLIENTORDERCD, AGENTID, HTORDERCD, MEMBERALIASID, MEMBERALIASNAME, TOMIDDLETIME, SCORE, INFANTREQUEST, SALESORDERID, FAILURECODE, RETURNORDERCD, TRAVELTYPE, UPGRADECLASS, NEWUPORDERCD, PSGTELFLAG, PRICEVALIDTIME, PRICEINVALIDTIME, RESERVEFLAG, UNITYORDERCD, UNITYITEMCD, REQUEST_PAYTIME, BOOKINGCLOSED, AIRLINEORDERCD, MBRSHIPCATEGORYCD, ISVALID, IP, AUTOCANCELFLAG, UK, SK, VERSION, AGENTORDERCD, MGUID, MGSID, CANCELTYPE, CANCELREASON) values \n";
StringBuffer sb = new StringBuffer(70605).append(head);

int count = 0;


try {
// read file content from file


FileReader reader = new FileReader("D:/task/2017/db_bak/t_at_ticketorder.sql");
BufferedReader br = new BufferedReader(reader);


String str = null;


while ((str = br.readLine()) != null) {
lineNum++;
// if (lineNum > 15882) {
// // System.out.println(str);
// } else {
// // System.out.println(str);
// }
// str = new String(str.getBytes(), "utf8");
if (str.indexOf("insert into") > -1) {
count++;
continue;
}
if (count == 1 || count % perBatchNum == 0) {


if (count > countNum) {
break;
}
sb.append(str.replace("values", "").replace("');", "'),").replaceAll("to_date", "str_to_date")
.replaceAll("dd-mm-yyyy hh24:mi:ss", "%d-%m-%Y %H:%i:%s")
.replaceAll("dd-mm-yyyy", "%d-%m-%Y").replaceAll("\\?", "").replaceAll("\\\\", "") + "\n");
if (count % perBatchNum == 0 && str.indexOf("');") > -1) {
insertSql(sb.substring(0, sb.length() - 2));
sb=null;//去掉内存占用情况会是锯齿状
System.gc();//去掉内存占用情况会是锯齿状

sb = new StringBuffer(70605).append(head);
}
str=null;//去掉内存占用情况会是锯齿状
continue;
}
sb.append(str.replace("values", "").replace("');", "'),").replaceAll("to_date", "str_to_date")
.replaceAll("dd-mm-yyyy hh24:mi:ss", "%d-%m-%Y %H:%i:%s").replaceAll("dd-mm-yyyy", "%d-%m-%Y")
.replaceAll("\\?", "").replaceAll("\\\\", "") + "\n");
str=null;//去掉内存占用情况会是锯齿状


}



br.close();
reader.close();


} catch (Exception e) {
System.out.println(count);
e.printStackTrace();
}
// return sb.substring(0, sb.length()-2);


}


public static void writeToFile(String sb) {
try {
// write string to file
FileWriter writer = new FileWriter("E:/mango/t_at_ticketorder/test2.txt");
BufferedWriter bw = new BufferedWriter(writer);
bw.write(sb);


bw.close();
writer.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}


public static void queryCount() {
sql = "select count(*) from t_at_ticketorder";// SQL语句
try {
preStmt = conn.prepareStatement(sql);// 准备执行语句
ret = preStmt.executeQuery(sql);// 执行语句,得到结果集
while (ret.next()) {
String uid = ret.getString(1);
System.out.println(uid);
} // 显示数据
ret.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值