[TOC]
# java監控工具使用
## jconsole
jconsole是一種集成了上面所有命令功能的可視化工具,可以分析jvm的內存使用情況和線程等信息。
**啟動jconsole**
通過JDK/bin目錄下的“jconsole.exe”啟動Jconsole后,將自動搜索出本機運行的所有虛擬機進程,不需要用戶使用jps來查詢了,雙擊其中一個進程即可開始監控。也可以“遠程連接服務器,進行遠程虛擬機的監控。”

遠程連接

**概覽頁面**

## jvisualvm
提供了和jconsole的功能類似,提供了一大堆的插件。
插件中,Visual GC(可視化GC)還是比較好用的,可視化GC可以看到內存的具體使用情況
# java內存模型
## 內存模型圖解
Java虛擬機在執行Java程序的過程中,會把它所管理的內存劃分為若干個不同的數據區。這些區域有各自的用途,以及創建和銷毀的時間,有的區域隨著虛擬機進程的啟動而存在,有的區域則依賴用戶線程的啟動和結束而建立和銷毀,我們可以將這些區域統稱為Java運行時數據區域。

如上圖所示,Java虛擬機運行時數據區域被分為五個區域:堆(Heap)、棧(Stack)、本地方法棧(Native Stack)、方法區(Method Area)、程序計數器(Program Count Register)
## 堆(Heap)
程序運行需要new出來大量的list和hashmap放在這里
對于大多數應用來說,Java Heap是Java虛擬機管理的內存的最大一塊,這塊區域隨著虛擬機的啟動而創建。在實際的運用中,我們創建的對象和數組就是存放在堆里面。如果你聽說線程安全的問題,就會很明確的知道Java Heap是一塊共享的區域,操作共享區域的成員就有了鎖和同步。
與Java Heap相關的還有Java的垃圾回收機制(GC),Java Heap是垃圾回收器管理的主要區域。程序猿所熟悉的新生代、老生代、永久代的概念就是在堆里面,現在大多數的GC基本都采用了分代收集算法。如果再細致一點,Java Heap還有Eden空間,From Survivor空間,To Survivor空間等。
Java Heap可以處于物理上不連續的內存空間中,只要邏輯上是連續的即可


Permanent space方法區,現在也歸到堆里面了,以前不歸入到堆里面
以前jvm不回收永久代,現在回收,因為永久代的那些靜態變量不會改變,但是會造成溢出,所以現在jvm開始回收了
新生代,老生代,是按照對象存活的年齡來劃分的
剛創建的對象放到新生代,然后jvm來了,對對象進行回收,然后把有價值的留下,from space 然后如果默認15次回收,你還在,我就把你放到老年代
Eden space是對象空間
有一塊To space永遠不會用,因為考慮到jvm回收的算法,To space就是jvm回收要復制內存的
## 程序計數器
程序執行到哪一行,不然程序會亂
## 棧(Stack)
方法的一些局部變量在棧里面
每個線程有自己的棧空間
相對于Java Heap來講,Java Stack是線程私有的,她的生命周期與線程相同。Java Stack描述的是Java方法執行時的內存模型,每個方法執行時都會創建一個棧幀(Stack Frame)用語存儲局部變量表、操作數棧、動態鏈接、方法出口等信息。從下圖從可以看到,每個線程在執行一個方法時,都意味著有一個棧幀在當前線程對應的棧幀中入棧和出棧。
## 本地方法棧(Native Stack)
jvm和本地平臺有調用的
本地方法棧(Native Stack)與Java虛擬機站(Java Stack)所發揮的作用非常相似,他們之間的區別在于虛擬機棧為虛擬機棧執行java方法(也就是字節碼)服務,而本地方法棧則為使用到Native方法服務。
## 方法區(Method Area)
類的定義放在方法區的永久區
方法區(Method Area)與堆(Java Heap)一樣,是各個線程共享的內存區域,它用于存儲虛擬機加載的**類信息,常量,靜態變量,即時編譯器編譯后的代碼**等數據。雖然Java虛擬機規范把方法區描述為堆的一個邏輯部分,但是她卻有一個別名叫做非堆(Non-Heap)。
分析下Java虛擬機規范,之所以把方法區描述為堆的一個邏輯部分,應該覺得她們都是存儲數據的角度出發的。一個存儲對象數據(堆),一個存儲靜態信息(方法區)。
在上文中,我們看到堆中有新生代、老生代、永久代的描述。
為什么我們將新生代、老生代、永久代三個概念一起說,那是因為HotSpot虛擬機的設計團隊選擇把GC分代收集擴展至方法區,或者說使用永久代來實現方法區而已。這樣HotSpot的垃圾收集器就能想管理Java堆一樣管理這部分內存。簡單點說就是HotSpot虛擬機中內存模型的分代,其中新生代和老生代在堆中,永久代使用方法區實現。根據官方發布的路線圖信息,現在也有放棄永久代并逐步采用Native Memory來實現方法區的規劃,在JDK1.7的HotSpot中,已經把原本放在永久代的字符串常量池移出。
## 總結
1. 線程私有的數據區域有:
Java虛擬機棧(Java Stack)
本地方法棧(Native Stack)
2. 線程共有的數據區域有:
堆(Java Heap)
方法區
# GC算法
## 標記-清除算法(Mark-Sweep)
1. 標記出所有需要回收的對象
2. 在標記完成后統一回收所有被標記的對象

缺點:一個是效率問題,標記和清除兩個過程的效率都不高;
另一個是空間問題,標記清除之后會產生大量不連續的內存碎片,空間碎片太多可能會導致以后在程序運行過程中
需要分配較大對象時,無法找到足夠的連續內存而不得不提前觸發另一次垃圾收集動作
## 復制算法(Copying)
1. 將可用內存按容量劃分為大小相等的兩塊,每次只使用其中的一塊。
2. 當這一塊的內存用完了,就將還存活著的對象復制到另外一塊上面,然后再把已使用過的內存空間一次清理掉

優點:這樣使得每次都是對整個半區進行內存回收,內存分配時也就不用考慮內存碎片等
復雜情況,只要移動堆頂指針,按順序分配內存即可,實現簡單,運行高效。只是這種算法的代價是將內存縮小為
了原來的一半,未免太高了一點。
缺點:復制收集算法在對象存活率較高時就要進行較多的復制操作,效率將會變低
### 標記-整理算法(Mark-Compact)
1. 標記
2. 讓所有存活的對象都向一端移動,然后直接清理掉端邊界以外的內存

### 分代收集算法(Generational?Collection)
1. 根據對象存活周期的不同將內存劃分為幾塊。
2. 一般是把Java堆分為新生代和老年代,這樣就可以根據各個年代的特點采用最適當的收集算法。
3. 在新生代中,每次垃圾收集時都發現有大批對象死去,只有少量存活,那就選用復制算法,只需要付出少量存活對象的復制成本就可以完成收集。
4. 老年代中因為對象存活率高、沒有額外空間對它進行分配擔保,就必須使用“標記—清理”或者“標記—整理”算法來進行回收。
# 垃圾回收器
## Serial收集器:
1. 是一個單線程的收集器,“Stop?The?World”
2. 對于運行在Client模式下的虛擬機來說是一個很好的選擇
3. 簡單而高效

## Serial?Old收集器
1. Serial收集器的老年代版本,它同樣是一個單線程收集器,使用“標記-整理”算法。
2. 主要意義也是在于給Client模式下的虛擬機使用。
3. 如果在Server模式下,那么它主要還有兩大用途:
? ? 一種用途是在JDK?1.5以及之前的版本中與Parallel?Scavenge收集器搭配使用,
? ? 另一種用途就是作為CMS收集器的后備預案,在并發收集發生Concurrent?Mode?Failure時使用。

## ParNew收集器
1. Serial收集器的多線程版本
2. 單CPU不如Serial
3. Server模式下新生代首選,目前只有它能與CMS收集器配合工作
4. 使用-XX:+UseConcMarkSweepGC選項后的默認新生代收集器,也可以使用-XX:+UseParNewGC選項來強制指定它。
5. `-XX:ParallelGCThreads`:限制垃圾收集的線程數。

## Parallel?Scavenge收集器
1. 吞吐量優先”收集器
2. 新生代收集器,復制算法,并行的多線程收集器
3. 目標是達到一個可控制的吞吐量(Throughput)。
4. 吞吐量=運行用戶代碼時間/(運行用戶代碼時間+垃圾收集時間),虛擬機總共運行了100分鐘,其中垃圾收集花掉1分鐘,那吞吐量就是99%。
5. 兩個參數用于精確控制吞吐量:
~~~
-XX:MaxGCPauseMillis是控制最大垃圾收集停頓時間
-XX:GCTimeRatio直接設置吞吐量大小
-XX:+UseAdaptiveSizePolicy:動態設置新生代大小、Eden與Survivor區的比例、晉升老年代對象年齡
~~~
6. 并行(Parallel):指多條垃圾收集線程并行工作,但此時用戶線程仍然處于等待狀態。
7. 并發(Concurrent):指用戶線程與垃圾收集線程同時執行(但不一定是并行的,可能會交替執行),用戶程序在繼續運行,而垃圾收集程序運行于另一個CPU上。
## Parallel?Old收集器
1. Parallel?Scavenge收集器的老年代版本,使用多線程和“標記-整理”算法。
2. 在注重吞吐量以及CPU資源敏感的場合,都可以優先考慮Parallel?Scavenge加Parallel?Old收集器。

## CMS收集器一款優秀的收集器
1. 以獲取最短回收停頓時間為目標的收集器。
2. 非常符合互聯網站或者B/S系統的服務端上,重視服務的響應速度,希望系統停頓時間最短的應用
3. 基于“標記—清除”算法實現的
4. CMS收集器的內存回收過程是與用戶線程一起并發執行的
5. 它的運作過程分為4個步驟,包括:
? ? ? ? 初始標記,“Stop?The?World”,只是標記一下GC?Roots能直接關聯到的對象,速度很快
? ? ? ? 并發標記,并發標記階段就是進行GC?RootsTracing的過程
? ? ? ? 重新標記,Stop?The?World”,是為了修正并發標記期間因用戶程序繼續運作而導致標記產生變動的那一部分對象的標記記錄,但遠比并發標記的時間短
? ? ? ? 并發清除(CMS?concurrent?sweep)
6. 優點:并發收集、低停頓
7. 缺點:
? ? ? ? ? ? 對CPU資源非常敏感。
? ? ? ? ? ? 無法處理浮動垃圾,可能出現“Concurrent?Mode?Failure”失敗而導致另一次Full?GC的產生。
? ? ? ? ? ? 一款基于“標記—清除”算法實現的收集器

## G1(Garbage-First)收集器
1. 當今收集器技術發展的最前沿成果之一
2. G1是一款面向服務端應用的垃圾收集器。
3. 優點:
? ? ? ??并行與并發:充分利用多CPU、多核環境下的硬件優勢
? ? ? ??分代收集:不需要其他收集器配合就能獨立管理整個GC堆
? ? ? ??空間整合:“標記—整理”算法實現的收集器,局部上基于“復制”算法不會產生內存空間碎片 ? ? ? ? ? ?
? ? ? ??可預測的停頓:能讓使用者明確指定在一個長度為M毫秒的時間片段內,消耗在垃圾收集上的時間不得超過N毫秒
4. G1收集器的運作大致可劃分為以下幾個步驟:
? ? ? ? 初始標記:標記一下GC?Roots能直接關聯到的對象,需要停頓線程,但耗時很短
? ? ? ? 并發標記:是從GC?Root開始對堆中對象進行可達性分析,找出存活的對象,這階段耗時較長,但可與用戶程序并發執行
? ? ? ? 最終標記:修正在并發標記期間因用戶程序繼續運作而導致標記產生變動的那一部分標記記錄
? ? ? ? 篩選回收:對各個Region的回收價值和成本進行排序,根據用戶所期望的GC停頓時間來制定回收計劃

# 垃圾收集器參數總結
收集器設置:
~~~
-XX:+UseSerialGC:年輕串行(Serial),老年串行(Serial Old)
-XX:+UseParNewGC:年輕并行(ParNew),老年串行(Serial Old)
-XX:+UseConcMarkSweepGC:年輕并行(ParNew),老年串行(CMS),備份(Serial Old)
-XX:+UseParallelGC:年輕并行吞吐(Parallel Scavenge),老年串行(Serial Old)
-XX:+UseParalledlOldGC:年輕并行吞吐(Parallel Scavenge),老年并行吞吐(Parallel Old)
~~~
收集器參數:
~~~
-XX:ParallelGCThreads=n:設置并行收集器收集時使用的CPU數。并行收集線程數。
-XX:MaxGCPauseMillis=n:設置并行收集最大暫停時間
-XX:GCTimeRatio=n:設置垃圾回收時間占程序運行時間的百分比。公式為1/(1+n)
-XX:+CMSIncrementalMode:設置為增量模式。適用于單CPU情況。
-XX:ParallelGCThreads=n:設置并發收集器年輕代收集方式為并行收集時,使用的CPU數。并行收集線程數。
~~~
# JVM參數列表
~~~
java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:NewRatio=4 -XX:SurvivorRatio=4 -XX:MaxPermSize=16m -XX:MaxTenuringThreshold=0
-Xmx3550m:最大堆內存為3550M。
-Xms3550m:初始堆內存為3550m。
此值可以設置與-Xmx相同,以避免每次垃圾回收完成后JVM重新分配內存。
~~~
~~~
-Xmn2g:設置年輕代大小為2G。
整個堆大小=年輕代大小 + 年老代大小 + 持久代大小。持久代一般固定大小為64m,所以增大年輕代后,將會減小年老代大小。此值對系統性能影響較大,Sun官方推薦配置為整個堆的3/8。
-Xss128k:設置每個線程的堆棧大小。
JDK5.0以后每個線程堆棧大小為1M,在相同物理內存下,減小這個值能生成更多的線程。但是操作系統對一個進程內的線程數還是有限制的,不能無限生成,經驗值在 3000~5000左右。
-XX:NewRatio=4:設置年輕代(包括Eden和兩個Survivor區)與年老代的比值(除去持久代)。設置為4,則年輕代與年老代所占比值為1:4,年輕代占整個堆棧的1/5
-XX:SurvivorRatio=4:設置年輕代中Eden區與Survivor區的大小比值。
設置為4,則兩個Survivor區與一個Eden區的比值為2:4,一個Survivor區占整個年輕代的1/6
-XX:MaxPermSize=16m:設置持久代大小為16m。
-XX:MaxTenuringThreshold=15:設置垃圾最大年齡。
如果設置為0的話,則年輕代對象不經過Survivor區,直 接進入年老代。對于年老代比較多的應用,可以提高效率。如果將此值設置為一個較大值,則年輕代對象會在Survivor區進行多次復制,這樣可以增加對象 再年輕代的存活時間,增加在年輕代即被回收的概論。
~~~
收集器設置
~~~
-XX:+UseSerialGC:設置串行收集器
-XX:+UseParallelGC:設置并行收集器
-XX:+UseParalledlOldGC:設置并行年老代收集器
-XX:+UseConcMarkSweepGC:設置并發收集器
~~~
垃圾回收統計信息
~~~
-XX:+PrintGC
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-Xloggc:filename
~~~
并行收集器設置
~~~
-XX:ParallelGCThreads=n:設置并行收集器收集時使用的CPU數。并行收集線程數。
-XX:MaxGCPauseMillis=n:設置并行收集最大暫停時間
-XX:GCTimeRatio=n:設置垃圾回收時間占程序運行時間的百分比。公式為1/(1+n)
~~~
并發收集器設置
~~~
-XX:+CMSIncrementalMode:設置為增量模式。適用于單CPU情況。
-XX:ParallelGCThreads=n:設置并發收集器年輕代收集方式為并行收集時,使用的CPU數。并行收集線程數。
~~~
# jvm案列
## 內存:
內存標簽相當于可視化的jstat命令,用于監視收集器管理的虛擬機內存(java堆和永久代)的變化趨勢。
我們通過下面的一段代碼體驗一下它的監視功能。運行時設置的虛擬機參數為:`-Xms100m -Xmx100m -XX:+UseSerialGC`,這段代碼的作用是以64kb/50毫秒的速度往java堆內存中填充數據。
~~~
public class TestMemory {
static class OOMObject {
public byte[] placeholder = new byte[64 * 1024];
}
public static void fillHeap(int num) throws Exception {
ArrayList<OOMObject> list = new ArrayList<OOMObject>();
for (int i = 0; i < num; i++) {
Thread.sleep(50);
list.add(new OOMObject());
}
System.gc();
}
public static void main(String[] args) throws Exception {
fillHeap(1000);
Thread.sleep(500000);
}
}
~~~

從圖中可以看出,運行軌跡成曲線增長,循環1000次后,雖然整個新生代Eden和Survivor區都基本上被清空了,但是老年代仍然保持峰值狀態,這說明,填充的數據在GC后仍然存活,因為list的作用域沒有結束。如果把System.gc();移到fillHeap(1000);后,就可以全部回收掉。
## 線程:
線程相當于可視化了jstack命令,遇到線程停頓時,可以使用這個也簽進行監控分析。線程長時間停頓的主要原因有:等待外部資源(數據庫連接等),死循環、鎖等待。下面的代碼將演示這幾種情況:
~~~
package cn.java.jvm;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class TestThread {
/**
* 死循環演示
*
* @param args
*/
public static void createBusyThread() {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("createBusyThread");
while (true)
;
}
}, "testBusyThread");
thread.start();
}
/**
* 線程鎖等待
*
* @param args
*/
public static void createLockThread(final Object lock) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("createLockThread");
synchronized (lock) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}, "testLockThread");
thread.start();
}
public static void main(String[] args) throws Exception {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
br.readLine();
createBusyThread();
br.readLine();
Object object = new Object();
createLockThread(object);
}
}
~~~
main線程:追蹤到需要鍵盤錄入
testBusyThread線程:線程阻塞在18行的while(true),直到線程切換,很耗性能
testLockThread線程:出于waitting狀態,等待notify
## 死鎖:
~~~
package cn.java.jvm;
public class TestDeadThread implements Runnable {
int a, b;
public TestDeadThread(int a, int b) {
this.a = a;
this.b = b;
}
@Override
public void run() {
System.out.println("createDeadThread");
synchronized (Integer.valueOf(a)) {
synchronized (Integer.valueOf(b)) {
System.out.println(a + b);
}
}
}
public static void main(String[] args) {
for (int i = 0; i < 100; i++) {
new Thread(new TestDeadThread(1, 2)).start();
new Thread(new TestDeadThread(2, 1)).start();
}
}
}
~~~
點擊檢查死鎖,會出現死鎖的詳情。

thread-5的鎖被thread-10持有,相反亦是,造成死鎖
- linux
- 常用命令
- 高級文本命令
- 面試題
- redis
- String
- list
- hash
- set
- sortedSet
- 案例-推薦
- java高級特性
- 多線程
- 實現線程的三種方式
- 同步關鍵詞
- 讀寫鎖
- 鎖的相關概念
- 多線程的join
- 有三個線程T1 T2 T3,保證順序執行
- java五種線程池
- 守護線程與普通線程
- ThreadLocal
- BlockingQueue消息隊列
- JMS
- 反射
- volatile
- jvm
- IO
- nio
- netty
- netty簡介
- 案例一發送字符串
- 案例二發送對象
- 輕量級RPC開發
- 簡介
- spring(IOC/AOP)
- spring初始化順序
- 通過ApplicationContextAware加載Spring上下文
- InitializingBean的作用
- 結論
- 自定義注解
- zk在框架中的應用
- hadoop
- 簡介
- hadoop集群搭建
- hadoop單機安裝
- HDFS簡介
- hdfs基本操作
- hdfs環境搭建
- 常見問題匯總
- hdfs客戶端操作
- mapreduce工作機制
- 案列-單詞統計
- 局部聚合Combiner
- 案列-流量統計(分區,排序,比較)
- 案列-倒排索引
- 案例-共同好友
- 案列-join算法實現
- 案例-求topN(分組)
- 自定義inputFormat
- 自定義outputFormat
- 框架運算全流程
- mapreduce的優化方案
- HA機制
- Hive
- 安裝
- DDL操作
- 創建表
- 修改表
- DML操作
- Load
- insert
- select
- join操作
- 嚴格模式
- 數據類型
- shell參數
- 函數
- 內置運算符
- 內置函數
- 自定義函數
- Transform實現
- 特殊分割符處理
- 案例
- 級聯求和accumulate
- flume
- 簡介
- 安裝
- 常用的組件
- 攔截器
- 案例
- 采集目錄到HDFS
- 采集文件到HDFS
- 多個agent串聯
- 日志采集和匯總
- 自定義攔截器
- 高可用配置
- 使用注意
- sqoop
- 安裝
- 數據導入
- 導入數據到HDFS
- 導入關系表到HIVE
- 導入表數據子集
- 增量導入
- 數據導出
- 作業
- 原理
- azkaban
- 簡介
- 安裝
- 案例
- 簡介
- command類型單一job
- command類型多job工作流flow
- HDFS操作任務
- mapreduce任務
- hive腳本任務
- hbase
- 簡介
- 安裝
- 命令行
- 基本CURD
- 過濾器查詢
- 系統架構
- 物理存儲
- 尋址機制
- 讀寫過程
- Region管理
- master工作機制
- 建表高級屬性
- 與mapreduce結合
- 協處理器
- 點擊流平臺開發
- 簡介
- storm
- 簡介
- 安裝
- 集群啟動及任務過程分析
- 單詞統計
- 并行度
- ACK容錯機制
- ACK簡介