性能优化可以在这几个方面下手, 流畅性 , 稳定性 , 包体积大小
稳定性优化
影响App稳定性常见的有两个场景 Crash 和 ANR,它会导致App异常退出。所以解决App的稳定性应该列为最高优先级。如何避免异常的发生,可以从这几个方面入手Linux的进程内存模型是由 用户空间 和 内核空间 组成。
在应用程序中,如果直接操作外围设备,访问时也不知道其他程序有没有在访问,也不知道哪一段可以用的,大家你争我抢的,都乱套了,而且也不安全。所以需要一位管理者--操作系统。操作系统将真实的物理地址隔离起来,给每个程序分配一段虚拟地址,通过mmap将真实地址和虚拟地址起来,比如虚拟地址是0x00,那么它真实的物理地址可能是0x1c。在真实物理地址它可能不是一段连续的地址,但是在虚拟地址是连续的就可以了。
虚拟空间还可以进行细分:
内核空间(进程管理,存储管理,文件管理,设备管理,网络系统等) ---------- 栈 FileMapping 堆 BSS Data text 复制代码
进程由n个线程组成,在JVM中,又对进程以线程为单位对内存进行划分。
线程的内存分配:
在操作系统看来,JVM是一个程序,而Java程序只是运行在程序上的程序,所以JVM需要模拟程序运行的环境。
(图片来源:csdn-骁兵)
public class Test { public void test(int b, int a) { int x = 6; if (b > 0) { String str = "VeCharm"; } int y = a; int c = y + b; } } ---------------- javac Test.java javap -v Test ---------------- class信息: Last modified 2019-3-31; size 347 bytes MD5 checksum b0e2fc2ec7a2d576136a693c77213446 Compiled from "Test.java" public class com.vecharm.lychee.sample.api.Test minor version: 0 major version: 52 flags: ACC_PUBLIC, ACC_SUPER Constant pool: ... { public com.vecharm.lychee.sample.api.Test(); descriptor: ()V flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return LineNumberTable: line 3: 0 public void test(int, int); descriptor: (II)V flags: ACC_PUBLIC Code: stack=2, locals=6, args_size=3 0: bipush 6 2: istore_3 3: iload_1 4: ifle 11 7: ldc #2 // String VeCharm 9: astore 4 11: iload_2 12: istore 4 14: iload 4 16: iload_1 17: iadd 18: istore 5 20: return LineNumberTable: line 7: 0 line 8: 3 line 9: 7 line 11: 11 line 12: 14 line 13: 20 StackMapTable: number_of_entries = 1 frame_type = 252 /* append */ offset_delta = 11 locals = [ int ] } SourceFile: "Test.java" 复制代码
public class Test { public void test(int b, int a) { int x = 6; if (b > 0) { String str = "VeCharm"; } int y = a; int c = y + b; } } ---------------- javac Test.java javap -v Test ---------------- class信息: ... Constant pool: #1 = Methodref #4.#14 // java/lang/Object."<init>":()V #2 = String #15 // VeCharm #3 = Class #16 // com/vecharm/lychee/sample/api/Test #4 = Class #17 // java/lang/Object #5 = Utf8 <init> #6 = Utf8 ()V #7 = Utf8 Code #8 = Utf8 LineNumberTable #9 = Utf8 test #10 = Utf8 (II)V #11 = Utf8 StackMapTable #12 = Utf8 SourceFile #13 = Utf8 Test.java #14 = NameAndType #5:#6 // "<init>":()V #15 = Utf8 VeCharm #16 = Utf8 com/vecharm/lychee/sample/api/Test #17 = Utf8 java/lang/Object ... SourceFile: "Test.java" 复制代码
CONSTANT_Utf8 | UTF-8编码的Unicode字符串 |
CONSTANT_Integer | int类型字面值 |
CONSTANT_Float | float类型字面值 |
CONSTANT_Long | long类型字面值 |
CONSTANT_Double | double类型字面值 |
CONSTANT_Class | 对一个类或接口的符号引用 |
CONSTANT_String | String类型字面值 |
CONSTANT_Fieldref | 对一个字段的符号引用 |
CONSTANT_Methodref | 对一个类中声明的方法的符号引用 |
CONSTANT_InterfaceMethodref | 对一个接口中声明的方法的符号引用 |
CONSTANT_NameAndType | 对一个字段或方法的部分符号引用 |
看到这想必就已经知道了一个从一个Java文件到内存是如何运作的了。类从加载到虚拟机内存中开始到卸载内存为止,它的整个生命周期包括:加载,验证,准备,解析,初始化,使用,和卸载7个阶段,其中验证,准备,解析3个部分被称为连接。
加载,验证,准备,初始化和卸载这5个阶段是确定的,类的加载过程是必须按照顺序来,而解析阶段这个可以在初始化之后开始,这是为了支持运行时绑定(动态绑定)。
说到底,编程就是编的只是数据和指令,来总结一下流程。
在内存模型中,我们需要重点关注的就是Heap。因为它是由我们来控制的,处理不当容易发生OOM。内存处理的步骤无非也就三个: 申请,整理,清除。管理内存打个比方就和管理卖戏票的,观众台也就几十个座位,都是宝贵的资源。vip大户,里边走,直接坐贵席。其他的买计时票看,每隔一定时间把到时的人清出去,但经常有人到时赖着不走,隔一段时间催他才走。有时候座满了,只能把到时的赖皮清出去,不想走可以交钱。有时候人家三五成群的买票,自然要调配一下,清理出一些连座的给客户对吧。如果是一大帮人来看,更是欢迎,vip里面请。
在Java的堆模型中划分为三个区。
管理对象的生命周期
生存还是毁灭,是通过这个对象到GC Root的可到达性来决定的。能作为GC Root的对象有四种。
引用类型有四种,强引用,软引用,弱引用,虚引用。
结语,限于篇幅,只是初略的整理了一下大致的流程,参考《深入Java虚拟机》等。