内存管理模型:堆、栈
***对象在heap 堆中分配内存
对象引用:指向其他对象在堆中的起始地址
非基本数据类型的变量等价于对象引用
每个对象可包含一组变量 ,每个变量可指向其他对象的引用
对象引用只指向一个其他对象,而一个对象可被多个其他对象所引用。
***基于堆和栈的内存管理都是动态分配
***栈:存储方法调用以及方法执行中的局部数据,遵循后进先出的原则,无法支持复杂数据类型
e.g:
***堆:自由模式的内存管理,动态分配,可管理复杂的动态数据结构
在一块内存里分为多个小块,每块包含一个对象,或者未被占用
代码中的一个变量可以在不同时间被关联到不同的内存对象上,无法在编译阶段确定。内存对象也可以进一步指向其他对象
e.g
GC、root、reachable、unreachable、live、dead
***GC:垃圾回收,识别“垃圾”对象,把其占用的内存加以回收
GC的成本度量标准:执行时间 + 内存消耗+ 延迟时间 + ……
***root:包含多个方面:静态区域的数据 + 寄存器 + 目前的执行栈中的数据所指向的内存对象
***reachable:从root可达的对象
***unreachable:从root不可达的对象
***live:从root 可达的对象
***dead:从root不可达的对象
在下图中:粉色的为live,黑色的为dead
GC的四种基本算法
***引用计数:在需要垃圾回收的对象上留下一个note,表明对该对象的实时参考数值,若该数值为0,则表明该对象需要被垃圾回收。即为每个object 存储一 个计数RC ,当有其他reference 指向它时,RC++ ;当其他reference 与其断开时,RC-- ;如果RC==0,则回收。
优点:简单、计算 代价分散 ,“幽灵时间”短
缺点:不全面(容易漏掉循环引用的对象)、并发支持较弱、占用额外内存空间……
***标记-清除:为每个object设定状态位(live/dead) 并记录,即mark 阶段;将标记为dead 的对象进行清理,即sweep 可阶段。
下图中用圈表示的对象由于其状态为为白(即为dead),需要被回收清理
优点:可收集循环产生的垃圾、指针操作没有运行时间开销、对mutator松散耦合,不需要移动对象
缺点:幽灵时间长;
内存中的每个位置都必须在该算法的扫描阶段进行检查,非常消耗时间,时间复杂度为O(heap)
***标记-整理:
优点:解决了Mark-Sweep采集的碎片问题,可用内存放在一个大块中
内存中对象的相对顺序保持不变
缺点:耗费许多时间收集,严重影响性能
***复制
该GC 策略与mark-compact 的区别在于:不是在同一个区域内进行整理,而是将live 对象全部复制到另一个区域。
优点:所有对象大小的分配都代价很低;
只处理live对象;
固定的空间开销;
可以收集循环产生的垃圾
简答实现合理高效的复制GC
缺点:停止和复制可能是破坏性
需要其他简单收集器的地址空间的两倍
复制大型对象的代价很大
长期数据可能会被重复复制
所有reference必须更新
moving对象可能会破坏mutator
广度优先复制可能会干扰局部模式
Java/JVM的内存管理模型:各区域、各区域的GC方法
Java GC 将堆分为不同的区域,各区域采用不同的GC 策略,以提高GC的效率
使用“-verbose:gc”在控制台或日志文件中输出JVM进行GC的全过程
***分为四个区域:Eden、Younggeneration、Old generation、Permanentgeneration
***Young Generation:只有一小部分对象可较长时间存活,故采用copy算法减少GC;针对young generation,使用minor GC进行垃圾收集
***Old Generation:这里的对象有很高的幸存度,使用Mark-Sweep或Mark-Compact,如果old generation 满了,则启动full GC
***当perm generation 满了之后,无法存储更多的元数据,也启动full GC
JVM GC性能调优:参数配置、GC模式选择
***堆的大小决定着VM将会以何种频度进行GC、每次GC的时间多长,这两个指标具体取值多少为“优”,需要针对特定应用进行分析。较大的heap会导致较少发生GC,但每次GC时间很长,如果根据程序需要来设置heap大小,则需要频繁GC,但每次GC的时间较短
***通过以下参数设置young generation的尺寸:
-XX:NewSize=<n>[g|m|k]:n为Young generation的大小,g/m/k代表gigabytes/megabytes/kilobytes.
-XX:MaxNewSize=<n>[g|m|k]:Young generation 的最大大小
-Xmn<n>[g|m|k]:设定Young generation的最初、最小、最大的大小
-XX:NewRatio=<n>:Young generation和Old generation的大小比例
-XX:SurvivorRatio=<n>:Eden 和 survivor space的大小比例
***old generation的尺寸不需要设置,根据其他各参数的取值可计算得到:
java-XX:NewSize=128m -XX:MaxNewSize=196m -Xms512m -Xmx1024m
GC模式选择
***-XX:+UseSerialGC :使用单个线程来执行所有的垃圾收集工作
***-XX:+UseParallelGC : 在young generation中并行执行次要集合,可以显著减少垃圾收集开销,但主要集合是使用单个线程执行的
***-XX:+UseConcMarkSweepGC : 收集终生代,并与执行应用程序同时执行大部分收集,在收集阶段,它会暂停一小段时间
***-XX:+UseTrainGC : 收集每一个小集合的Old generation,并尽量减少大停顿
Java性能调优工具:jstat, jmap, jhat, Visual VM, MAT
***jstat:获取JVM 的heap 使用和GC 的性能统计数据
***jmap:输出内存中的对象分布情况
jmap -dump:导出heap dump
jmap -heap:获得Java堆的信息
jmap -histo:获得堆的类特定直方图
jmap -permstat:永久代数统计
***jhat:导出heap dump,浏览/查询其中的对象分布情况;是个heap dump文件的浏览和查询工具
***jstack:获取Java线程的stack trace
***Visual VM:
配置文件性能和内存使用
采取并显示线程转储
采取并浏览堆转储
在线和离线分析核心转储
***MAT(Eclipse Memory Analyzer):内存堆导出文件的分析工具
Java代码调优的设计模式
***singleton:某些类在应用运行期间只需要一个实例
强制client 只能创建一个object 实例,避免因为new 操作所带来的时空性能(尤其是GC)的损失,也便于复用
设置静态变量来存储单一实例对象,将构造器设置为private,从而client无法new,在构造器中new新实例,提供静态方法来获取单一实例对象
进一步提升性能:在需要的时候再new,而非提前构造出来
***Flyweight:该模式允许在应用中不同部分共享使objects,降低大量objects带来的时空代价
内部特征:不管在什么场合使用该object,内部特征都不变
外部特征:不是固定的,需要在不同场合context分别指派/计算其值
***Flyweight VS Singleton
单例:不区分各场合下的不同表现形式,统一用一个实例表示
轻量对象:同一个事物,具有多种不同表现形式
Flyweight在object层面的复用比Singleton更灵活
***Prototype:通过克隆而非new来创建object
引用拷贝:
对象拷贝:
浅拷贝:使用一个已知实例的成员变量对新创建实例的成员变量逐个赋值。
只复制对象本身,但不复制对象对外的引用
表示外泄
深拷贝:类的拷贝方法不仅要复制对象的所有非引用成员变量值(简单数据类型),还要为引用类型(对象)的成员变量创建新的实例,并且初始化为原对象的值。
***object pool:不要扔掉object,留着后续复用
代价:原本可被GC的对象,现在要留在pool中,导致内存浪费——用空间换时间
String Constant Pool字符串常量池