at java.lang.String.intern(Native Method)```
#### 2.4.4本机直接内存溢出 ####
- DirectMemory容量可通过-XX:MaxDirectMemorySize指定,如果不指定,则默认与Java堆最大值(-Xmx指定)一样
## 第3章 垃圾收集器与内存分配策略 ##
### 3.1 概述 ###
- 当需要排查各种内存溢出、内存泄漏问题时,当垃圾收集成为系统达到更高并发量的瓶颈时,我们就需要对这些“自动化”的技术实施必要的监控和调节。
- 程序计数器、虚拟机栈、本地方法栈3个区域随线程而生,随线程而灭,
JAVA堆和方法区不一样,一个接口中的多个实现类需要的内存可能不一样,一个方法中的多个分支需要的内存也可能不一样。
### 3.2 对象已死 ###
- 堆里存放的几乎所有的对象,如何判断对象是否还存活
#### 3.2.1 引用计数算法 ####
- 实现简单,判定效率高,大部分情况下是一个不错的算法,微软公司的COM技术,使用ActionScript3的FlashPlayer,Python语言等
- 缺点很难解决对象之间相互循环引用的问题
#### 3.2.2 可达性分析 ####
- 在主流的商用程序语言(Java、C#,甚至包括前面提到的古老的Lisp)的主流实现中,都是称通过可达性分析(Reachability Analysis)来判定对象是否存活的。
- 个算法的基本思路就是通过一系列的称为"GC Roots"的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连(用图论的话来说,就是从GC Roots到这个对象不可达)时,则证明此对象是不可用的
- 可作为GC ROOTS 的对象包含下面
- 虚拟机栈(栈帧中的本地变量表)中引用的对象。
- 方法区中类静态属性引用的对象。
- 方法区中常量引用的对象。
- 本地方法栈中JNI(即一般说的Native方法)引用的对象。

#### 3.2.3 引用 ####
- Java中的引用的定义很传统:如果reference类型的数据中存储的数值代表的是另外一块内存的起始地址,就称这块内存代表着一个引用。
- 强引用:就是指在程序代码之中普遍存在的,类似"Object obj=new Object()"这类的引用,只要强引用还存在,垃圾收集器永远不会回收掉被引用的对象。
- 软引用:是用来描述一些还有用但并非必需的对象。对于软引用关联着的对象,在系统将要发生内存溢出异常之前,将会把这些对象列进回收范围之中进行第二次回收。如果这次回收还没有足够的内存,才会抛出内存溢出异常。在JDK 1.2之后,提供了SoftReference类来实现软引用。
- 弱引用:也是用来描述非必需对象的,但是它的强度比软引用更弱一些,被弱引用关联的对象只能生存到下一次垃圾收集发生之前。当垃圾收集器工作时,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。在JDK 1.2之后,提供了WeakReference类来实现弱引用。
- 虚引用也称为幽灵引用或者幻影引用,它是最弱的一种引用关系。一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。为一个对象设置虚引用关联的唯一目的就是能在这个对象被收集器回收时收到一个系统通知。在JDK 1.2之后,提供了PhantomReference类来实现虚引用。
#### 3.2.5 回收方法区 ####
- Java虚拟机规范中确实说过可以不要求虚拟机在方法区实现垃圾收集,而且在方法区中进行垃圾收集的“性价比”一般比较低:在堆中,尤其是在新生代中,常规应用进行一次垃圾收集一般可以回收70%~95%的空间,而永久代的垃圾收集效率远低于此。
- 永久代的垃圾收集主要回收两部分内容:废弃常量和无用的类
- 回收废弃常量:与回收Java堆中的对象非常类似。以常量池中字面量的回收为例,假如一个字符串"abc"已经进入了常量池中,但是当前系统没有任何一个String对象是叫做"abc"的,换句话说,就是没有任何String对象引用常量池中的"abc"常量,也没有其他地方引用了这个字面量,如果这时发生内存回收,而且必要的话,这个"abc"常量就会被系统清理出常量池。常量池中的其他类(接口)、方法、字段的符号引用也与此类似。
- 判定一个常量是否是“废弃常量”比较简单,而要判定一个类是否是“无用的类”的条件则相对苛刻许多。类需要同时满足下面3个条件才能算是“无用的类”:
- 该类所有的实例都已经被回收,也就是Java堆中不存在该类的任何实例。
- 加载该类的ClassLoader已经被回收。
- 该类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。
- 是否对类进行回收,HotSpot虚拟机提供了-Xnoclassgc参数进行控制,还可以使用-verbose:class以及-XX:+TraceClassLoading、-XX:+TraceClassUnLoading查看类加载和卸载信息,其中-verbose:class和-XX:+TraceClassLoading可以在Product版的虚拟机中使用,-XX:+TraceClassUnLoading参数需要FastDebug版的虚拟机支持。
### 3.3 垃圾收集算法 ###
- 各个平台的虚拟机操作内存的方法又各不相同
#### 3.3.1 标记-清除算法 ####
- 算法分为“标记”和“清除”两个阶段:首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象,它的标记过程其实在前一节讲述对象标记判定时已经介绍过了
- 一个是效率问题,标记和清除两个过程的效率都不高;另一个是空间问题,标记清除之后会产生大量不连续的内存碎片,空间碎片太多可能会导致以后在程序运行过程中需要分配较大对象时,无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作。标记—清除算法的执行过

#### 3.3.2 复制算法 ####
- 将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这块使用完了,将活着的对象复制到另外一块,清理掉使用过的内存空间,这样使得每次回收都是整个内存半区回收,缺点:将内存缩小为原来的一半!


#### 3.3.3 标记-整理算法 ####
- 标记方法同"标记-清除算法"一致,但是再垃圾回收时,并不直接回收,而是将存活的对象移动到一块,然后在统一清除
/home/care4u
#### 3.3.4 分代收集算法 ####
- 根据对象的存活周期将内存划分几块区域,一般JAVA 堆分为新生代和老年代 ,新生代中每次收集大批对象死亡,所以采用复制算法。老年代中对象存活比较长,所以采用标记算法
### 3.4 HotSpot的算法实现 ###
#### 3.4.1 枚举根节点 ####
- 从可达性分析中从GC Roots节点找引用链这个操作为例,可作为GC Roots的节点主要在全局性的引用(例如常量或类静态属性)与执行上下文(例如栈帧中的本地变量表)中,现在很多应用仅仅方法区就有数百兆,如果要逐个检查这里面的引用,那么必然会消耗很多时间。
#### 3.4.2 安全点 ####
- HotSpot也的确没有为每条指令都生成OopMap,前面已经提到,只是在“特定的位置”记录了这些信息,这些位置称为安全点(Safepoint),即程序执行时并非在所有地方都能停顿下来开始GC,只有在到达安全点时才能暂停。
- 安全点的选择:指令序列复用,例如方法调用、循环跳转、异常跳转等,所以具有这些功能的指令才会产生Safepoint。
#### 3.4.3 安全区域 ####
- 安全区域是指在一段代码片段之中,引用关系不会发生变化。在这个区域中的任意地方开始GC都是安全的。我们也可以把Safe Region看做是被扩展了的Safepoint。
## 3.5 垃圾收集器 ##
- 如果说收集算法是内存回收的方法论,那么垃圾收集器就是内存回收的具体实现
- 这个虚拟机所包含的所有收集器

### 3.5.1 Serial收集器 ###
- 这个收集器是一个单线程的收集器,但它的“单线程”的意义并不仅仅说明它只会使用一个CPU或一条收集线程去完成垃圾收集工作,更重要的是在它进行垃圾收集时,必须暂停其他所有的工作线程,直到它收集结束

### 3.5.2 ParNew收集器 ###
- ParNew收集器其实就是Serial收集器的多线程版本,除了使用多条线程进行垃圾收集之外,其余行为包括Serial收集器可用的所有控制参数(例如:-XX:SurvivorRatio、-XX:PretenureSizeThreshold、-XX:HandlePromotionFailure等)、收集算法、Stop The World、对象分配规则、回收策略等都与Serial收集器完全一样,在实现上,这两种收集器也共用了相当多的代码。

>**●并行(Parallel):指多条垃圾收集线程并行工作,但此时用户线程仍然处于等待状态。**
>**●并发(Concurrent):指用户线程与垃圾收集线程同时执行(但不一定是并行的,可能会交替执行),用户程序在继续运行,而垃圾收集程序运行于另一个CPU上。**
### 3.5.3 Parallel Scavenge收集器 ###
- Parallel Scavenge收集器是一个新生代收集器,它也是使用复制算法的收集器,又是并行的多线程收集器
- Parallel Scavenge收集器的特点是它的关注点与其他收集器不同,CMS等收集器的关注点是尽可能地缩短垃圾收集时用户线程的停顿时间,而Parallel Scavenge收集器的目标则是达到一个可控制的吞吐量(Throughput)。所谓吞吐量就是CPU用于运行用户代码的时间与CPU总消耗时间的比值,即吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间),虚拟机总共运行了100分钟,其中垃圾收集花掉1分钟,那吞吐量就是99%。
- Parallel Scavenge收集器提供了两个参数用于精确控制吞吐量,分别是控制最大垃圾收集停顿时间的-XX:MaxGCPauseMillis参数以及直接设置吞吐量大小的-XX:GCTimeRatio参数。
- -XX:+UseAdaptiveSizePolicy 打开这个参数后不需要指定,新生代大小、Eden与Survivor区的比例(-XX:SurvivorRatio)、晋升老年代对象年龄(-XX:PretenureSizeThreshold)等细节参数了
- 自适应调节策略也是Parallel Scavenge收集器与ParNew收集器的一个重要区别。
### 3.5.4 Serial Old收集器 ###
- Serial Old是Serial收集器的老年代版本,它同样是一个单线程收集器,使用“标记-整理”算法。这个收集器的主要意义也是在于给Client模式下的虚拟机使用

### 3.5.5 Parallel Old收集器 ###
- Parallel Old是Parallel Scavenge收集器的老年代版本,使用多线程和“标记-整理”算法

### 3.5.6 CMS收集器 ###
- CMS 收集器是一种以获取最短回收停顿时间为目标的收集器。响应速度快
- 初始标记(CMS initial mark)
并发标记(CMS concurrent mark)
重新标记(CMS remark)
并发清除(CMS concurrent sweep)
- 初始标记和并发标记,仍然需要停止。初始标记只是标记一下GC root能直接关联到的对象

- 缺点:
- 1.CMS收集器对CPU资源非常敏感。其实,面向并发设计的程序都对CPU资源比较敏感,CMS默认启动的回收线程数是(CPU数量+3)/4
- 2.CMS收集器无法处理浮动垃圾(Floating Garbage),可能出现"Concurrent Mode Failure"失败而导致另一次Full GC的产生
- -- -XX:CMSInitiatingOccupancyFraction 设置老年代使用多少空间时激活CMS收集器,太高容易导致回收失败,性能降低
- 采用‘标记-清除’算法,碎片空间过多,对于大对象的分配很麻烦,往往老年代还有很大的空间,但是无法找到连序的空间进行分配
- XX:+UseCMSCompactAtFullCollection用于CMS收集器顶不住要进行FullGC时开启内存碎片的合并整理过程,内存整理的过程是无法并发的,空间碎片问题没有了,但停顿时间不得不变长。
- -XX:CMSFullGCsBeforeCompaction,这个参数是用于设置执行多少次不压缩Full GC后,跟着来一次带压缩的(默认值为0,表示每次进入Full GC时都进行碎片整理)
### 3.5.7 G1收集器 ###
- G1是一款面向服务端应用的垃圾收集器
- 并行与并发:G1能充分利用多CPU、多核环境下的硬件优势,使用多个CPU(CPU或者CPU核心)来缩短Stop-The-World停顿的时间,部分其他收集器原本需要停顿Java线程执行的GC动作,G1收集器仍然可以通过并发的方式让Java程序继续执行。
- 分代收集:与其他收集器一样,分代概念在G1中依然得以保留。虽然G1可以不需要其他收集器配合就能独立管理整个GC堆,但它能够采用不同的方式去处理新创建的对象和已经存活了一段时间、熬过多次GC的旧对象以获取更好的收集效果。
- 空间整合:与CMS的“标记—清理”算法不同,G1从整体来看是基于“标记—整理”算法实现的收集器,从局部(两个Region之间)上来看是基于“复制”算法实现的,但无论如何,这两种算法都意味着G1运作期间不会产生内存空间碎片,收集后能提供规整的可用内存。这种特性有利于程序长时间运行,分配大对象时不会因为无法找到连续内存空间而提前触发下一次GC。
- 可预测的停顿:这是G1相对于CMS的另一大优势,降低停顿时间是G1和CMS共同的关注点,但G1除了追求低停顿外,还能建立可预测的停顿时间模型,能让使用者明确指定在一个长度为M毫秒的时间片段内,消耗在垃圾收集上的时间不得超过N毫秒,这几乎已经是实时Java(RTSJ)的垃圾收集器的特征了
- 初始标记(Initial Marking)
并发标记(Concurrent Marking)
最终标记(Final Marking)
筛选回收(Live Data Counting and Evacuation)

### 垃圾收集相关参数 ###


## 3.6 内存分配与回收策略 ##
- 给对象分配内存以及回收分配给对象的内存
### 3.6.1 对象优先在Eden分配 ###
- 大多数情况下,对象在新生代Eden区中分配。当Eden区没有足够空间进行分配时,虚拟机将发起一次Minor GC。
- -XX:+PrintGCDetails 开启打印内存回收日志,并且在内存退出的时候输出当前内存的分配情况
- 新生代GC(Minor GC):指发生在新生代的垃圾收集动作,因为Java对象大多都具备朝生夕灭的特性,所以Minor GC非常频繁,一般回收速度也比较快。
- 老年代GC(Major GC/Full GC):指发生在老年代的GC,出现了Major GC,经常会伴随至少一次的Minor GC(但非绝对的,在Parallel Scavenge收集器的收集策略里就有直接进行Major GC的策略选择过程)。Major GC的速度一般会比Minor GC慢10倍以上。
### 3.6.2 大对象直接进入老年代 ###
- 所谓的大对象是指,需要大量连续内存空间的Java对象,最典型的大对象就是那种很长的字符串以及数组
- -XX:PretenureSizeThreshold 大于该对面值得对面直接在老年代分配,避免Eden区以及Survivor 区域发生大量的内存复制
### 3.6.3 长期存活的对象将进入老年代 ###
- 在Eden区域出生,经过第一次minor GC后,能够移动到survivor空间,并且对象年龄设为1,经过15次minor GC 后进入老年代(默认)
- -XX:MaxTenuringThreshold 设置年龄
### 3.6.4 动态对象年龄判定 ###
- survivor空间中相同年龄的对象大小和大于survivor一半,年龄大于等于该年龄的对象直接进入老年代
### 3.6.5 空间分配担保 ###
- 查看老年代连序空间是否大于新生代所有对象空间,成立进行minor GC
如果不成立,则虚拟机会查看HandlePromotionFailure设置值是否允许担保失败
- 如果允许,那么会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于,将尝试着进行一次Minor GC,尽管这次Minor GC是有风险的;如果小于,或者HandlePromotionFailure设置不允许冒险,那这时也要改为进行一次Full GC。
## 第4章 虚拟机性能监控与故障处理工具 ##
### 4.1 概述 ###
- 适当合理的运用工具,尽心内存的分析。给一个系统定位问题的时候,知识、经验是关键基础,数据是依据,工具是运用知识处理数据的手段。这里说的数据包括:运行日志、异常堆栈、GC日志、线程快照(threaddump/javacore文件)、堆转储快照(heapdump/hprof文件)等
### 4.2 JDK命令行工具 ###


#### 4.2.1 jps:虚拟机进程状况工具 ####

#### 4.2.2 jstat:虚拟机统计信息监视工具 ####
- jstat(JVM Statistics Monitoring Tool)是用于监视虚拟机各种运行状态信息的命令行工具。它可以显示本地或者远程[1]虚拟机进程中的类装载、内存、垃圾收集、JIT编译等运行数据,在没有GUI图形界面,只提供了纯文本控制台环境的服务器上,它将是运行期定位虚拟机性能问题的首选工具。
- `jstat[option vmid[interval[s|ms][count]]] `

#### 4.2.3 jinfo:Java配置信息工具 ####
- jinfo(Configuration Info for Java)的作用是实时地查看和调整虚拟机各项参数。使用jps命令的-v参数可以查看虚拟机启动时显式指定的参数列表,但如果想知道未被显式指定的参数的系统默认值,除了去找资料外,就只能使用jinfo的-flag选项进行查询了
#### 4.2.4 jmap:Java内存映像工具 ####
- jmap(Memory Map for Java)命令用于生成堆转储快照(一般称为heapdump或dump文件)
- jmap的作用并不仅仅是为了获取dump文件,它还可以查询finalize执行队列、Java堆和永久代的详细信息,如空间使用率、当前用的是哪种收集器等。

#### 4.2.5 jhat:虚拟机堆转储快照分析工具 ####
- n JDK提供jhat(JVM Heap Analysis Tool)命令与jmap搭配使用,来分析jmap生成的堆转储快照。jhat内置了一个微型的HTTP/HTML服务器,生成dump文件的分析结果后,可以在浏览器中查看。
- jhat [file]
#### 4.2.6 jstack:Java堆栈跟踪工具 ####
- jstack(Stack Trace for Java)命令用于生成虚拟机当前时刻的线程快照(一般称为threaddump或者javacore文件)。线程快照就是当前虚拟机内每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间等待等都是导致线程长时间停顿的常见原因。

#### 4.2.7 HSDIS:JIT生成代码反汇编 ####

1)虚拟机启动参数只限制了Java堆为100MB,没有指定-Xmn参数,能否从监控图中估计出新生代有多大?
> 图4-6显示Eden空间为27 328KB,因为没有设置-XX:SurvivorRadio参数,所以Eden与Survivor空间比例为默认值8:1,整个新生代空间大约为27 328KB×125%=34 160KB。
2)为何执行了System.gc()之后,图4-6中代表老年代的柱状图仍然显示峰值状态,代码需要如何调整才能让System.gc()回收掉填充到堆中的对象?
>执行完System.gc()之后,空间未能回收是因为List<OOMObject>list对象仍然存活,fillHeap()方法仍然没有退出,因此list对象在System.gc()执行时仍然处于作用域之内[2]。如果把System.gc()移动到fillHeap()方法外调用就可以回收掉全部内存。
### 4.3 JDK的可视化工具 ###
#### 4.3.1 JConsole:Java监视与管理控制台 ####
- JConsole(Java Monitoring and Management Console)是一种基于JMX的可视化监视、管理工具。它管理部分的功能是针对JMX MBean进行管理,由于MBean可以使用代码、中间件服务器的管理控制台或者所有符合JMX规范的软件进行访问,所以本节将会着重介绍JConsole监视部分的功能。
#### 4.3.2 VisualVM:多合一故障处理工具 ####
- VisualVM(All-in-One Java Troubleshooting Tool)是到目前为止随JDK发布的功能最强大的运行监视和故障处理程序,并且可以预见在未来一段时间内都是官方主力发展的虚拟机故障处理工具。

- 启动: jvisualvm.exe

- 2.生成、浏览堆转储快照
- 在VisualVM中生成dump文件有两种方式,可以执行下列任一操作:
在“应用程序”窗口中右键单击应用程序节点,然后选择“堆Dump”。
在“应用程序”窗口中双击应用程序节点以打开应用程序标签,然后在“监视”标签中单击“堆Dump”。
- 需要保存则右键直接另存为保存下来

- 3.分析程序性能
- 在Profiler页签中,VisualVM提供了程序运行期间方法级的CPU执行时间分析以及内存分析,做Profiling分析肯定会对程序运行性能有比较大的影响,所以一般不在生产环境中使用这项功能。
- CPU 统计每个方法执行次数、执行耗时;
- 内存分析,则会统计每个方法关联的对象数以及这些对象所占的空间
- 4.BTrace动态日志跟踪
- BTrace[3]是一个很“有趣”的VisualVM插件,本身也是可以独立运行的程序。它的作用是在不停止目标程序运行的前提下,通过HotSpot虚拟机的HotSwap技术[4]动态加入原本并不存在的调试代码

## 5.2 案例分析 ##
### 5.2.1 高性能硬件上的程序部署策略 ###
- 在高性能硬件上部署程序,目前主要有两种方式:
> - 通过64位JDK来使用大内存。
> - 使用若干个32位虚拟机建立逻辑集群来利用硬件资源。
### 5.2.2 集群间同步导致的内存溢出 ###