内存优化(三):Profile内存检测工具

本文探讨了Java虚拟机内存结构,包括堆、方法区、运行时常量池和直接内存,以及内存垃圾回收机制。重点分析了Java堆溢出、永久代溢出、栈溢出和内存抖动现象及其解决方案。同时,举例说明了字符串拼接可能导致的内存问题和内存分配走势分析。
摘要由CSDN通过智能技术生成
  • JAVA堆:对象内存分配的地方,内存垃圾回收的主要区域,所有线程共享。可分为新生代,老生代。

  • 方法区:用于存储已经被JVM加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。Hotspot中的“永久代”。

  • 运行时常量池:方法区的一部分,存储常量信息,如各种字面量、符号引用等。

  • 直接内存:并不是JVM运行时数据区的一部分, 可直接访问的内存, 比如NIO会用到这部分。

按照JVM规范,除了程序计数器不会抛出OOM外,其他各个内存区域都可能会抛出OOM。

最常见的OOM情况有以下三种:

  • java.lang.OutOfMemoryError: Java heap space

java堆内存溢出,此种情况最常见,一般由于内存泄露或者堆的大小设置不当引起。对于内存泄露,需要通过内存监控软件查找程序中的泄露代码,而堆大小可以通过虚拟机参数-Xms,-Xmx等修改。

  • java.lang.OutOfMemoryError: PermGen space

java永久代溢出,即方法区溢出了,一般出现于大量Class或者jsp页面,或者采用cglib等反射机制的情况,因为上述情况会产生大量的Class信息存储于方法区。此种情况可以通过更改方法区的大小来解决,使用类似-XX:PermSize=64m -XX:MaxPermSize=256m的形式修改。另外,过多的常量尤其是字符串也会导致方法区溢出。

  • java.lang.StackOverflowError

不会抛OOM error,但也是比较常见的Java内存溢出。JAVA虚拟机栈溢出,一般是由于程序中存在死循环或者深度递归调用造成的,栈大小设置太小也会出现此种溢出。可以通过虚拟机参数-Xss来设置栈的大小。

内存抖动

短时间内大量的对象被创建,导致可用内存不足,从而引起频繁gc回收对象,这种已用内存忽高忽低的现象就叫内存抖动。由于gc的过程会 “stop the world” 停止其他的一切工作,gc太频繁无疑会造成界面卡顿,而且gc回收后可能会产生内存碎片,如果这时其他线程需要申请大块内存还有可能发生OOM,所以内存抖动的情况必须要避免。

什么情况会出现内存抖动呢?

img

for循环内使用了+进行字符串拼接

这是很常见的字符串拼接操作,通过查看字节码文件可以看出+的拼接字符串实际上是创建StringBuilder对象进行拼接,这样就会出现大量创建对象频繁GC,导致内存抖动。

内存抖动一定是锯齿状吗?

看下面一个实例,实现IOS小菊花的功能

public class IOSStyleLoadingView extends View {

private Context mContext;

private float mStrokeWidth;

private float northwestXStart = 264.57f;

private float northwestYStart = 264.71f;

private float northwestXEnd = 193.72f;

private float northwestYEnd = 194.14f;

private float northXStart = 300;

private float northYStart = 250;

private float northXEnd = 300;

private float northYEnd = 150;

private float notheastXStart = 335.25f;

private float notheastYStart = 264.54f;

private float notheastXEnd = 405.76f;

private float notheastYEnd = 193.63f;

private float eastXStart = 350;

private float eastYStart = 300f;

private float eastXEnd = 450;

private float eastYEnd = 300;

private float southeastXStart = 335.36f;

private float southeastYStart = 335.34f;

private float southeastXEnd = 406.10f;

private float southeastYEnd = 406.02f;

private float southXStart = 300.03f;

private float southYStart = 345f;

private float southXEnd = 300;

private float southYEnd = 450;

private float southwestXStart = 264.68f;

private float southwestYStart = 335.39f;

private float southwestXEnd = 194.06f;

private float southwestYEnd = 406.19f;

private float westXStart = 250;

private float westYStart = 300;

private float westXEnd = 150;

private float westYEnd = 300;

String colorStr[] = new String[]{

“#ffff00”,

“#ff3300”,

“#ccff00”,

“#ff00cc”,

“#ccffff”,

“#cc99ff”,

“#99ff66”,

“#993300”

};

private ValueAnimator valueAnimator;

private int currentColor = 0;

public IOSStyleLoadingView(Context context, AttributeSet attrs) {

super(context, attrs);

this.mContext = context;

this.mStrokeWidth = UIUtils.dp2px(this.mContext, 5);

}

@Override

protected void onDraw(Canvas canvas) {

super.onDraw(canvas);

Sample test1=new Sample(“测试1”);

Paint paint = new Paint();

paint.setAntiAlias(true);

paint.setStrokeWidth(this.mStrokeWidth);

paint.setStyle(Paint.Style.STROKE);

paint.setStrokeCap(Paint.Cap.ROUND);

Path p0 = new Path();

paint.setColor(Color.parseColor(colorStr[0]));

p0.moveTo(northwestXStart, northwestYStart);

p0.lineTo(northwestXEnd, northwestYEnd);

canvas.drawPath(p0, paint);

Path p1 = new Path();

paint.setColor(Color.parseColor(colorStr[1]));

p1.moveTo(northXStart, northYStart);

p1.lineTo(northXEnd, northYEnd);

canvas.drawPath(p1, paint);

Path p2 = new Path();

paint.setColor(Color.parseColor(colorStr[2]));

p2.moveTo(notheastXStart, notheastYStart);

p2.lineTo(notheastXEnd, notheastYEnd);

canvas.drawPath(p2, paint);

Path p3 = new Path();

paint.setColor(Color.parseColor(colorStr[3]));

p3.moveTo(eastXStart, eastYStart);

p3.lineTo(eastXEnd, eastYEnd);

canvas.drawPath(p3, paint);

Path p4 = new Path();

paint.setColor(Color.parseColor(colorStr[4]));

p4.moveTo(southeastXStart, southeastYStart);

p4.lineTo(southeastXEnd, southeastYEnd);

canvas.drawPath(p4, paint);

Path p5 = new Path();

paint.setColor(Color.parseColor(colorStr[5]));

p5.moveTo(southXStart, southYStart);

p5.lineTo(southXEnd, southYEnd);

canvas.drawPath(p5, paint);

Path p6 = new Path();

paint.setColor(Color.parseColor(colorStr[6]));

p6.moveTo(southwestXStart, southwestYStart);

p6.lineTo(southwestXEnd, southwestYEnd);

canvas.drawPath(p6, paint);

Path p7 = new Path();

paint.setColor(Color.parseColor(colorStr[7]));

p7.moveTo(westXStart, westYStart);

p7.lineTo(westXEnd, westYEnd);

canvas.drawPath(p7, paint);

}

@Override

protected void onAttachedToWindow() {

super.onAttachedToWindow();

startAnimation();

}

public void startAnimation() {

valueAnimator = ValueAnimator.ofInt(7, 0);

valueAnimator.setDuration(400);

valueAnimator.setRepeatCount(ValueAnimator.INFINITE);

valueAnimator.setInterpolator(new LinearInterpolator());

valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

@Override

public void onAnimationUpdate(ValueAnimator animation) {

if ((int) animation.getAnimatedValue() != currentColor){

String b[] = new String[colorStr.length];

for (int c = 0, size = colorStr.length - 1; c < size; c++) {

b[c + 1] = colorStr[c];

}

b[0] = colorStr[colorStr.length - 1];

colorStr = b;

invalidate();

currentColor = (int) animation.getAnimatedValue();

}

}

});

valueAnimator.start();

}

}

img

内存分配

这是一个看起来相对平滑的内存走势图,但是也能看出内存一直在增加,不知道为什么我的内存分配Allocations一直为0,希望看到的小伙伴留言解答一下。

上面的代码有三个问题:

1、不断创建String对象,如下代码

paint.setColor(Color.parseColor(color[0]));

点击进去看源码:

public static int parseColor(@Size(min=1) String colorString) {

if (colorString.charAt(0) == ‘#’) {

// Use a long to avoid rollovers on #ffXXXXXX

// 这里调用了String类的substring方法

long color = Long.parseLong(colorString.substring(1), 16);

if (colorString.length() == 7) {

// Set the alpha value

color |= 0x00000000ff000000;

} else if (colorString.length() != 9) {

throw new IllegalArgumentException(“Unknown color”);

}
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

尾声

开发是需要一定的基础的,我是08年开始进入Android这行的,在这期间经历了Android的鼎盛时期,和所谓的Android”凉了“。中间当然也有着,不可说的心酸,看着身边朋友,同事一个个转前端,换行业,其实当时我的心也有过犹豫,但是我还是坚持下来了,这次的疫情就是一个好的机会,大浪淘沙,优胜劣汰。再等等,说不定下一个黄金浪潮就被你等到了。

  • 330页 PDF Android核心笔记

  • 几十套阿里 、字节跳动、腾讯、华为、美团等公司2020年的面试题

  • PDF和思维脑图,包含知识脉络 + 诸多细节

  • Android进阶系统学习视频

《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门即可获取!

浪潮就被你等到了。

  • 330页 PDF Android核心笔记

[外链图片转存中…(img-5LyzAmEa-1711942579200)]

  • 几十套阿里 、字节跳动、腾讯、华为、美团等公司2020年的面试题

[外链图片转存中…(img-yD5L1xnw-1711942579200)]

[外链图片转存中…(img-UEkZxY7w-1711942579200)]

  • PDF和思维脑图,包含知识脉络 + 诸多细节

[外链图片转存中…(img-NPf4QNLn-1711942579200)]

  • Android进阶系统学习视频

[外链图片转存中…(img-ZQuK6cvj-1711942579200)]

《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门即可获取!
  • 16
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值