8. 面向性能的软件构造
8.1 软件构造性能指标
性能度量指标:更少的时间、更高的吞吐率、对资源使用更少
8.2 内存性能与垃圾回收
三种内存管理的基本模式:静态/堆/栈.Java的内存管理模型。垃圾回收的基本算法。JVM垃圾回收及其调优。
8.2.1 系统和应用级别的内存管理
系统内存管理:计算机系统中物理内存,虚拟内存的知识。
应用级别的内存管理:内存分配、垃圾回收。
8.2.2 三种对象管理的模型
静态内存分配,在编译阶段就已经确定好了内存分配。
在运行时动态分配内存,建立新的内存对象。(基于堆和栈的内存管理都是动态分配)(堆和栈是两种)
- 静态内存分配
不支持递归,不支持可变长的复杂数据类型。 - 基于栈的内存分配
支持递归,但是不支持可变长的复杂数据类型。 - 基于堆的内存分配
某些对象延续的时间比创建它的方法所延续的时间更长(所以stack不行)
递归的数据结构,长度可变的数据结构(所以静态方法和stack都不行)
经常要使用不限定长度的数据结构
8.2.3 Java内存模型
当某个对象不再有reference指向它,如何删除对象、释放内存。每个线程有自己的栈,管理其内部数据,互相不可见。所有局部的基本数据类型(int,byte…)都在栈上创建,但是即使是局部的对象也是在堆上创建。
Method Area: 用于存储被JVM加载的类信息、常量、静态变量等。HotSpot JVM中用Permanent Area (Perm)实现该区域,并作为heap的一部分。Java 8之后改名为Metaspace (使用native memory)
8.2.4 垃圾回收
8.2.4.1 三种内存回收的模式
在静态内存分配模式下,无需进行内存回收:所有都是已确定的。
在栈上进行内存空间回收:按block(某个方法)整体进行。
在堆上进行内存空间回收:最复杂,无法预知哪个对象不会再被使用了
8.2.4.2 可达对象和不可达对象
Root–根对象,根对象可达-可达对象,活对象(保留);根对象不可达-不可达对象,死对象(回收)
第一种回收方式:从root对象开始进行有向图的搜索,将图分为root可达部分和root不可达部分,后者将被进行内存回收。
8.2.4.3 垃圾回收的定义
垃圾回收器根据对象的“活性”(从root的可达性)来决定是否回收该对象的内存。垃圾回收GC:识别“垃圾”对象,把其占用的内存加以回收。
垃圾回收的花费:执行时间,对使用者内存的影响,延迟时间
8.2.4.4 运行时没有自动回收机制
- 无法使用引用,所有的对象传递都是基于复制,过于浪费空间。
- new()和delete()需要成对出现。并且需要完成析构函数。
- 处理对象共享问题十分复杂。
- 显式释放的危险性,释放过多(需要使用时发现已经被回收了)释放过少(内存空间不够了)浪费大量debug的时间。
8.2.4.5 运行时拥有自动回收机制
没有自动回收会使程序出现更多的问题,内存泄漏(没有处理,永远死在了内存里)和悬空指针(提前处理,再次引用的时候出现错误)
8.2.5 基础的垃圾回收算法
引用计数(Reference counting)标记-清除(Mark-Sweep)标记-整理(Mark-Compact)复制(Copying)
8.2.5.1 引用计数
一种统计调用数目的机制。
引用计数的基本思想:为每个object存储一个计数RC,当有其他reference指向它时,RC++;当其他reference与其断开时,RC–;如果RC==0,则回收它 (及其所有指向的object)。
优点:简单、计算代价分散,“幽灵时间”短 -> 0 ,对count的修改是在每行代码执行后进行,而非集中进行。
缺点:不全面(容易漏掉循环引用的对象)、并发支持较弱、占用额外内存空间。
8.2.5.2 标记-清除
标记清除:为每个object设定状态位(live/dead)并记录,即mark阶段;将标记为dead的对象进行清理,即sweep阶段。标记阶段:从根对象根据调用关系进行遍历,没有遍历到的对象标记为黑色。然后清除。
有点:所耗空间较小。
缺点:时间过长,幽灵时间较长。并且容易产生碎片内存。
8.2.5.3 标记-整理
相较于标记清除,多了一步整理的操作。
优点:避免碎片化;
缺点:时间消耗太长,影响程序本身
8.2.5.4 碎片整理和复制
该GC策略与标记-整理的区别在于:不是在同一个区域内进行整理,而是将活对象全部复制到另一个区域。
优点:没有碎片,容易实现
缺点:复制过程容易造成破坏,复制过程开销过大,存活时间较久的对象可能会被多次复制。
8.2.6 JVM的垃圾回收
Java GC将堆分为不同的区域,各区域采用不同的GC策略,以提高GC的效率。
- 对象刚刚创建的时候,都被分配在伊甸园中。针对年轻代:只有一小部分对象可较长时间存活,故采用copy算法减少GC代价。
- 针对年老代:这里的对象有很高的幸存度,使用Mark-Sweep或Mark-Compact算法 。
- 伊甸园和老年代之间有一定的空间S0和S1是提供给两个区域对象交换使用的。
- 只有当某个区域不能再为对象分配内存时(满),才启动GC。每次年轻代GC使用Minor GC进行垃圾回收,如果历经多次minor GC仍存活下来,将其copy到老年代。
- 如果老年代满了,则启动full GC
- 不写了!!!!!!!!!!!!考完试了