查看压测曲线图,其实内存的情况整体看还好。

 但是不能看total,要看实际的走势, 1 5 9 代表测试第几轮,可以发现在前几轮测试中一直在增长, 突然下去是因为进程被kill了

android设置相机预览方向 android 相机预览内存观测_Graphic

 

 根据压测步骤挨个筛选泄漏的地方

 结果:

 操作步骤: 打开 camera,  切换到前置虚化模式, 静止, 查看内存一直在增长且无回落

lilei@rj-lilei0 MINGW64 /
$ adb shell dumpsys meminfo com.sec.android.app.camera | grep "Graphics\|Java\|Native\|TOTAL\|EGL mtrack"
  Native Heap    49010    48988        0       38    87248    84507     2740
   EGL mtrack    11332    11332        0        0
        TOTAL   187808   170151    12748       62    99741    90856     8884
           Java Heap:     8544
         Native Heap:    48988
            Graphics:    31771
               TOTAL:   187808       TOTAL SWAP PSS:       62

lilei@rj-lilei0 MINGW64 /
$ adb shell dumpsys meminfo com.sec.android.app.camera | grep "Graphics\|Java\|Native\|TOTAL\|EGL mtrack"
  Native Heap    52458    52436        0       38    90464    87711     2752
   EGL mtrack    11696    11696        0        0
        TOTAL   192300   174635    12748       62   103555    94658     8896
           Java Heap:     9204
         Native Heap:    52436
            Graphics:    32135
               TOTAL:   192300       TOTAL SWAP PSS:       62
lilei@rj-lilei0 MINGW64 /
$ adb shell dumpsys meminfo com.sec.android.app.camera | grep "Graphics\|Java\|Native\|TOTAL\|EGL mtrack"
  Native Heap    54434    54412        0       38    92400    89375     3024
   EGL mtrack    11471    11471        0        0
        TOTAL   194271   176602    12748       62   105834    96665     9168
           Java Heap:     9424
         Native Heap:    54412
            Graphics:    31910
               TOTAL:   194271       TOTAL SWAP PSS:       62

lilei@rj-lilei0 MINGW64 /
$ adb shell dumpsys meminfo com.sec.android.app.camera | grep "Graphics\|Java\|Native\|TOTAL\|EGL mtrack"
  Native Heap    57518    57496        0       38    95392    92570     2821
   EGL mtrack    11701    11701        0        0
        TOTAL   198324   180649    12748       62   109442   100476     8965
           Java Heap:    10144
         Native Heap:    57496
            Graphics:    32145
               TOTAL:   198324       TOTAL SWAP PSS:       62

  
 可以看到的是, java 和 native 一起增长,但是 java增长较小, native增长较快(每次2M)

1.  因为native增长较快的关系,所以觉得是算法泄漏, 就直接从 detect 算法入手,

private final ImageReader.OnImageAvailableListener mPreviewOnImageAvailableListener = new ImageReader.OnImageAvailableListener() {
        @Override
        public void onImageAvailable(ImageReader reader) {
	      
	      将检测算法全部注释掉,查看是否还增长

	      其实中间是有个乌龙的,
                         本来注释掉, 发现不增长了, 
			 最后弄了一早上挨个排查, 最后发现又增长。

	      原因是: 最后排查泄漏的地方在 faceView中的逻辑, 如果检测到人脸就不会增长了,没有检测到人脸就会一直增长。
	      所以测试手法和场景很重要.

	}

2. 预览一直增加的话,既然 detect 算法没有一直增加,那还能有什么地方用到算法呢?

  一直检测的就剩下人脸的逻辑(里面都是java逻辑),说是试试?结果果然是这里

@Override
public void onCaptureCompleted(CameraCaptureSession session,
          CaptureRequest request, TotalCaptureResult result) {
       int id = (int) result.getRequest().getTag();
        if (id == getMainCameraId()) {
        updateFocusStateChange(result);
        Face[] faces = result.get(CaptureResult.STATISTICS_FACES);

	//------- 结果注释掉下面的 faceView 的逻辑,内存果然正常了 --------
        if (faces != null && isBsgcDetecionOn()) {
            updateFaceView(faces, getBsgcInfo(result, faces.length));
        } else {
            updateFaceView(faces, null);
        }

 就很奇怪, 这里都是java的一些逻辑,为何会让native 增加??
 根据FaceView的逻辑代码排查, 发现只有这个模式, 会显示一个 toast提示(太近,太远,效果正常,find a face 等)

  

3. 定位到出问题的点:    setBlurToastShow()这个方法导致的

public void setBlurToastShow() {
        ...
        mRotateTextTips = RotateTextSingleBokehTips.makeText(mActivity, R.string.pref_camera2_show_singleBokeh_title,Toast.LENGTH_SHORT,true);
        mRotateTextTips.showSingleBokeh();
     }



    Public class RotateTextSingleBokehTips {

	private static HashSet<RotateLayout> mToasts = new HashSet<RotateLayout>();
  
	private RotateTextSingleBokehTips(Activity activity, int duration) {
	  mLayoutRoot = (ViewGroup) activity.getWindow().getDecorView();
	  LayoutInflater inflater = activity.getLayoutInflater();
	  View v = inflater.inflate(R.layout.rotate_text_singlebokeh_tips, mLayoutRoot);
	  mToast = (RotateLayout) v.findViewById(R.id.rotate_singlebokeh_text);
	  mToast.setOrientation(mOrientation, false);
	  mHandler = new Handler();
	  mDuration = duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY;
	}

	public static RotateTextSingleBokehTips makeText(Activity activity, int textResourceId, int duration, boolean showAtBottom) {
	    RotateTextSingleBokehTips rt = new RotateTextSingleBokehTips(activity, textResourceId, duration);
	     rt.setLayoutBootm(showAtBottom);
	    return rt;
	}
        .......
	.......

	public void showSingleBokeh() {
	   setSingleBokehTextViewLayout(mToast);
	   mToast.setTag(SHOW_AT_BOTTOM);
	   mToasts.add(mToast);
	   mToast.setVisibility(View.VISIBLE);
	}

	public void removeSingleBokeh(){
	    CameraUtil.fadeOut(mToast);
	    mLayoutRoot.removeView(mToast);
	    mToasts.remove(mToast);
	   mToast = null;
	}

   }

  仔细看一下 RotateTextSingleBokehTips 这个类的逻辑
  里面有一个 静态的 HashSet mToast , 因为每次 RotateTextSingleBokehTips.makeText 的逻辑是每次new 一个新的View
  然后add 进  mToast 中, 所以就会一直增加。

  但是这个也就是个 java对象泄漏吧, 为啥会导致native 泄漏?

  解决的方案2选1:

  

1. 每次调用 showSingleBokeh 之前, 都掉用  removeSingleBokeh , 确保清空了 HashSet 对每次new的View的持有,导致无法释放
     (这个方案就是我为什么之前发现一会增加,一会不增加, 原来检测到人脸的时候, 弹出的toast 会先remove掉,再show)
      
     public void setFaces(Face[] faces, ExtendedFace[] extendedFaces) {
       
        if (mPause) return;
        ...
        mFaces = faces;
        mExFaces = extendedFaces;
        //有人脸时
        if (!mBlocked && (mFaces != null) && (mFaces.length > 0) && mCameraBound != null) {
            invalidate();
            if(mCurrentModule instanceof SingleBokehCaptureModule){
                SingleBokehCaptureModule module = (SingleBokehCaptureModule)mCurrentModule;
		//-------------------------------所以这种情况下不会泄漏-----------------------------
                //会先 remove之前的View
                module.setBlurToastDispear();
                //再显示自定的toast
                module.setBlurShow(true);
                if(mRectForSingleBokeh != null && mRectForSingleBokeh.width() > 0) {
                    int width = (int) mRectForSingleBokeh.width();
                    int height = (int) mRectForSingleBokeh.height();
                    if (width < 100 || height < 100) {
                        module.showBokehStatusMessage(SingleBokehCaptureModule.MSG_TOO_FAR);
                    } else if (width > 500 || height > 500) {
                        module.showBokehStatusMessage(SingleBokehCaptureModule.MSG_TOO_NEAR);
                    } else {
                        module.showBokehStatusMessage(SingleBokehCaptureModule.MSG_DEPTH_EFFECT_SUCCESS);
                    }
                }
            }
        //无人脸时
        }else {
            if (mCurrentModule instanceof SingleBokehCaptureModule){
                SingleBokehCaptureModule module = (SingleBokehCaptureModule)mCurrentModule;
		//-------------------------------会泄漏-----------------------------
                //显示自定的toast
                module.setBlurToastShow();
                module.setBlurShow(false);
                module.showBokehStatusMessage(SingleBokehCaptureModule.MSG_SUBJECT_NOT_FOUND);
            }
        }
       }

  2. 不重复创建,只创建一个,切换模式时 remove
       public void setBlurToastShow() {
        ...
        if(mRotateTextTips == null){
              mRotateTextTips = RotateTextSingleBokehTips.makeText(mActivity, R.string.pref_camera2_show_singleBokeh_title,Toast.LENGTH_SHORT,true);
        }
        mRotateTextTips.showSingleBokeh();
    }
    最后切换模式的地方 onDestory ,再清楚 HashSet


4. 最后最后, native 的增加的问题, 为何 java对象会导致 native持续增加, 并且很快

  dump了hprof文件,进行查看

 

android设置相机预览方向 android 相机预览内存观测_EGL_02

 

可以看到2个泄漏的地方:

  1. mToast 持有过多的 mToast(RotateLayout)

  2. mToast(RotateLayout) 持有 decorView 导致  inputMethodManager 的 native泄漏

     原因:mLayoutRoot = (ViewGroup) activity.getWindow().getDecorView();
           //将 DecorView 传了进去, 持有 decorView 导致  inputMethodManager 的 native泄漏
           View v = inflater.inflate(R.layout.rotate_text_singlebokeh_tips, mLayoutRoot)

       //如果传 null, 经过试验, 则不会导致 native泄漏
       View v = inflater.inflate(R.layout.rotate_text_singlebokeh_tips, null)