移动学习 AndroidStudio内存优化分析—alloc文件分析

通过Androidstudio分析移动学习不做任何操作时的内存分配及占用情况

1、点击“start Allocation Tracking"5-10秒后再次点击,生成.allc文件:


2、用group by Method的方式看线程 6814:


3、是netty线程在接收read消息时站的内存,主要是newHeapBuffer()构造函数初始化时占用内存,查了一下:

Netty包含三种ByteBuf:

  1. HEAP BUFFERS:
  2. The most used type is the ByteBuf that stores its data in the heap space of the JVM. This is done by storing it in a backing array. This type is fast to allocate and also de-allocate when you re not using a pool. It also offers a way to directly access the backing array, which may make it easier to interact with legacy code .
  3. DIRECT BUFFERS
  4. Another ByteBuf implementation is the direct one. Direct means that it allocates the memory directly, which is outside the heap . You won't see its memory usage in your heap space. You must take this into account when calculating the maximum amount of memory your application will use and how to limit it, as the max heap size won t be enough. Direct buffers on the other side are optimal when it s time to transfer data over a socket. In fact, if you use a nondirect buffer, the JVM will make a copy of your buffer to a direct buffer internally before sending it over the socket.
  5. The down side of direct buffers is that they re more expensive to allocate and de-allocate compared to heap buffers. This is one of the reasons why Netty supports pooling, which makes this problem disappear. Another possible down side can be that you re no longer able to access the data via the backing array, so you ll need to make a copy of the data if it needs to work with legacy code that requires this.
  6. COMPOSITE BUFFERS
  7. The last ByteBuf implementation you may be confronted with is the CompositeByteBuf. This does exactly what its name says; it allows you to compose different ByteBuf instances and provides aview over them. The good thing is you can also add and remove them on-the-fly, so it s kind of like a List.
  8. For example, a message could be composed of two parts: header and body. In a modularized application, the two parts could be produced by different modules and assembled later when the message is sent out. Also, you may use the same body all the time and just change the header. So it would make sense here to not allocate a new buffer every time.
  9. This would be a perfect fit for a CompositeByteBuf asno memory copy will be needed and the same API could be used

4、HeapBuffer是Netty用来进行data存储的主要类,暂时管不了

5、线程 633,发现在mainLoop()中有两处内存泄露:


6、是查询数据库是,cursor的内存泄露,原来的cursor写法:

Cursor cursor = mDatabase.rawQuery("select courseId, courseName, courseTimeId, createTime, leaveEnd, leaveId, leaveReason, leaverId, leaveStart, leaveStatus, orgName, typeId, typeName, userName from QingjiaTable where leaverId = ?", new String[]{userid});
while (cursor.moveToNext()) {
	QingjiaModel qm = new QingjiaModel();
	....
	qm.setUserName(cursor.getString(0...13));
	qmList.add(qm);
}
cursor.close();

7现改为:

Cursor cursor =null;
try{
	cursor = mDatabase.rawQuery("select courseId, courseName, courseTimeId, createTime, leaveEnd, leaveId, leaveReason, leaverId, leaveStart, leaveStatus, orgName, typeId, typeName, userName from QingjiaTable where leaverId = ?", new String[]{userid});
	while (cursor != null && cursor.moveToNext()) {
		QingjiaModel qm = new QingjiaModel();
		qm.setUserName(cursor.getString(0...13));
		qmList.add(qm);
	}
}finally{
	if(cursor != null){
		cursor.close();		
	}
}

8、再次运行程序,完美!

9、看线程6766,有点傻眼,有一半都是关于simpleDateFomate类的方法,挨个看吧,先看run():505



11、主要都是service中的dateFormat和StringBuffer,而StringBuffer用的不多,内存和分配次数也没占多少,全都是DateFormat

12、线程6766中,除run()505外基本都是DateFormat函数分配的内存太多,要人命啊。找了找service中的代码:

class TimerRunnable implements RunFunction {
	@SuppressWarnings("deprecation")
	@Override
	public void timerRun() {// 主业务逻辑
		Log.i(TAG, "智能教学管理系统:后台运行,当前运行时间:" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
		.........

13、每10秒执行一次的时候都会调用一次,而在相关业务逻辑的计算时都是new出来的。好可怕

14、找到了一篇文章:https://www.bbsmax.com/A/kvJ3eb3ndg/ simpleDateFormat是非线程安全的,在多并发时会有问题,照着博主的代码改一下:

public class ThreadLocalDateUtil {
    private static final String date_format = "yyyy-MM-dd HH:mm:ss";
    private static ThreadLocal<DateFormat> threadLocal = new ThreadLocal<DateFormat>();  
    public static DateFormat getDateFormat()
    {
        DateFormat df = threadLocal.get();
        if(df==null){
            df = new SimpleDateFormat(date_format);
            threadLocal.set(df);
        }
        return df;
    }  
 
    public static String formatDate(Date date) throws ParseException {
        return getDateFormat().format(date);
    }
 
    public static Date parse(String strDate) throws ParseException {
        return getDateFormat().parse(strDate);
    }
}

15、再次运行程序 ,不完美。

parse()方法经常返回空,造成接下来程序出错。

而且内存开销没有减少,还是40-45M左右,干脆改成这个:

public class ThreadLocalDateUtil {
    private static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    public static String format(Date date){
        synchronized(sdf){
            return sdf.format(date);
        }

    }

    public static Date parse(String strDate){
        try{
            synchronized(sdf){
                return sdf.parse(strDate);
            }
        }catch (ParseException e){
            e.printStackTrace();
        }
        return null;
    }
}
16、再次运行程序,完美,不会出现parse()返回空的情况

并且内存瞬间减小10M,稳定在30M左右

而且线程中也没有其他占内存项,就是parse()方法占点内存开销。

比之前好太多,开心:




猜你喜欢

转载自blog.csdn.net/jbb0403/article/details/74529704
今日推荐