第八章第一节软件构造性能的度量原理
1. 内存管理模式
静态
-定义:静态内存是指在程序开始运行时由编译器分配的内存,它的分配是在程序开始编译时完成的,不占用CPU资源。
-程序中的各种变量,在编译时系统已经为其分配了所需的内存空间,当该变量在作用域内使用完毕时,系统会自动释放所占用的内存空间;
-不支持递归,不支持动态创建可变长的复杂数据类型;
-在程序执行期内实体至多关联一个运行时对象
eg: 基本类型,数组
动态-基于栈
-栈定义:方法调用和局部变量的存储位置,保存基本类型
-如果一个方法被调用,它的栈帧被放到调用栈的顶部
-栈帧保存方法的状态,包括执行哪行代码以及所有局部变量的值
-栈顶始终是当前运行方法
-一个实体可以在运行时连续地连接到多个对象,并且运行时机制以堆栈中的后进先出顺序分配和释放这些对象
-栈无法支持复杂数据结构
动态-基于栈
-堆定义:在一块内存里分为多个小块,每块包含一个对象,或者未被占用
-自由模式的内存管理,动态分配,可管理复杂的动态数据结构
-代码中的一个变量可以在不同时间被关联到不同的内存对象上,无法在编译阶段确定。内存对象也可以进一步指向其他对象
2. Java Memory Model
-
线程栈:每个线程创建时JVM都会为其创建一个工作内存(有些地方称为栈空间),
-
每个线程有自己的栈,管理其局部数据,各栈之间彼此不可见
-
所有局部的基本数据类型都在栈上创建
-
多线程之间传递数据,是通过复制而非引用
-
-
堆:所有对象(即使是局部变量的object)都是在堆上创建的
-
主内存可被多线程共享
-
-
一个基本类型的局部变量,一直被保存在线程栈中
-
一个对象类型的局部变量,其引用保存在线程栈 中,对象本身存在堆中
-
对象可能包含方法,这些方法可能包含局部变量。这些局部变量存储在线程栈上,并且该方法所属的对象存储在堆
-
对象的原始成员变量存储在堆上。如果一个成员变量是一个对象的引用,它将被存储在堆
-
静态类变量保存在堆上
Garbage Collection
在静态内存分配模式下,无需进行内存回收:所有都是已确定的
在栈上进行内存空间回收:按block(某个方法)整体进行
在heap 上进行内存空间回收,最复杂——无法提前预知某个object 是否已经变得无用。
对象的"活性":可达/ 不可达
静态区域的数据
Root包括:
–寄存器
–目前的执行栈中的
-数据所指向的内存对象
-活对象:从root 可达的对象
-死对象:从root 不可达
垃圾回收器根据对象的"活性"( 从root 的可达性) 来决定是否回收该对象的内存
"死"的对象就是需要回收的"垃圾"
GC的四种算法
1.Reference counting引用计数
引用计数的基本思想:为每个object 存储一 个计数RC ,当有其他reference 指向它时,RC++ ;当其他reference 与其断开时,RC-- ;如果RC==0,则回收它
引用计数方法的优点:简单、计算代价分散 ,"幽灵时间"短
缺点容易漏掉循环引用的对象
2.Mark-Sweep 标记
基本思想:为每个object设定状态位(live/dead)并记录,即mark阶段;将标记为dead的对象进行清理,即sweep可阶段。
优点:可以处理循环调用,指针操作无开销,对象不变
缺点:复杂度为O(heap),高 堆的占用比高时影响性能,容易造成碎片,需要找到root
3.Mark-Compact 标记-整理
基本思想:将存活对象都向一端移动,然后清理掉端边界以外的内存。
优点:可以处理循环调用,指针操作无开销,对象不变
缺点:复杂度为O(heap),高 堆的占用比高时影响性能,容易造成碎片,需要找到root
4.Copying 复制
基本思想:为了解决Mark-Sweep算法的缺陷,Copying算法就被提了出来。它将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块的内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用的内存空间一次清理掉,这样一来就不容易出现内存碎片的问题。
优势:运行高效、不易产生内存碎片
缺点:复制花费大量的时间,牺牲内存空间
-verbose:gc"
使用该参数在控制台或日志文件中输出JVM进行GC的全过程
The permanent generation:java类元数据、interned String、类的静态变量
针对年轻代: 只有一小部分对象可较长时间存活,故采用copy 算法减少GC
针对年老代:这里的对象有很高的幸存度,使用Mark-Sweep 或Mark-Compact
只有当某个区域不能再为对象分配内存时(满),才启动GC
针对young generation,使用minor GC 进行垃圾收集
Minor GC所需时间较短
如果历经多次minor GC仍存活下来,将其copy到old generation
如果old generation 满了,则启动full GC
当perm generation 满了之后,无法存储更多的元数据,也启动full GC
-XX: NewSize=<n>[g|m|k]
-XX: MaxNewSize=<n>[g|m|k]
-Xmn<n>[g|m|k]
-XX:NewRatio=<n>
-XX:SurvivorRatio=<n>
java –Xms 1024M –Xmx 2048M
System.gc()
GC模式选择
-
增长或收缩年轻代或老年代的空间时需要Full GC
-
Full GC可能会降低吞吐量并导致超出期望的延迟
-
串行收集器(-XX:+UseSerialGC):使用单个线程执行所有垃圾收集工作
-
并行收集器(-XX:+UseParallelGC):并行执行Minor GC,显著减少垃圾收集开销
-
并发低暂停收集器(-XX:+UseConcMarkSweepGC):收集持久代,与执行应用程序同时执行大部分收集,在收集期间会暂停一小段时间
-
增量低暂停收集器(-XX:+UseTrainGC):收集每个Minor的部分老年代,并尽量减少Major的大停顿
-
-verbose:gc:打印GC信息
Dynamic Program Analysis
Java性能调优工具
-
Jstat:获取JVM的Heap使用和GC的性能统计数据,命令如-gcutil
-
Jmap:输出内存中的对象分布情况 如:jmap -clstats
-
Jhat:导出heap dump,浏览/查询其中的对象分布情况
-
jstack:获取Java线程的stack trace 具体用途如下:
-
定位线程出现长时间停顿的原因,如多线程间死锁、死循环、请求外部资源导致的长时间等待等。
-
线程出现停顿的时候通过jstack来查看各个线程的调用堆栈,就可以知道没有响应的线程到底在后台做什么事情,或者等待什么资源。
-
-
Visual VM:提供了一个可视化界面,用于查看Java应用程序在JVM上运行时的详细信息,使用各种技术,包括jvmstat,JMX,Serviceability Agent(SA)和Attach API等
-
MAT:内存堆导出文件的分析工具,生成饼状图等,能够对问题发生时刻的系统内存状态获取一个整体印象,找到最有可能导致内存泄露的对象,进一步查看其是否有异常行为。
静态分析:使用抽象的输入值
动态分析:要使用具体的输入值
-定位线程出现长时间停顿的原因,如多线程间死锁、死循环、请求外部资源
导致的长时间等待等。
–线程出现停顿的时候通过jstack来查看各个线程的调用堆栈,就可以知道没有响应的线程到底在后台做什么事情,或者等待什么资源。