JVM分析工具

1 機器性能統計工具

1.1 shell工具

查看CPU信息(數量 型號)
cat /proc/cpuinfo | grep name | cut -f2 -d: | uniq -c
1 QEMU Virtual CPU version 1.1.2
查看內存使用情況
cat /proc/meminfo

MemTotal(內存總量): 8059828 kB
MemFree(可用內存): 252324 kB
SwapTotal(swap總量): 2096440 kB
SwapFree(swap可用大小): 239180 kB

MemTotal:        8059828 kB
MemFree:          252324 kB
Buffers:            7408 kB
Cached:           153836 kB
SwapCached:        72760 kB
Active:          6181000 kB
Inactive:        1424668 kB
Active(anon):    6115000 kB
Inactive(anon):  1336368 kB
Active(file):      66000 kB
Inactive(file):    88300 kB
Unevictable:           0 kB
Mlocked:               0 kB
SwapTotal:       2096440 kB
SwapFree:         239180 kB
Dirty:               196 kB
Writeback:             0 kB
AnonPages:       7371704 kB
Mapped:            36560 kB
Shmem:              6940 kB
Slab:              71112 kB
SReclaimable:      28328 kB
SUnreclaim:        42784 kB
KernelStack:        7504 kB
PageTables:        53772 kB
NFS_Unstable:          0 kB
Bounce:                0 kB
WritebackTmp:          0 kB
CommitLimit:     6126352 kB
Committed_AS:    7230044 kB
VmallocTotal:   34359738367 kB
VmallocUsed:       26540 kB
VmallocChunk:   34359709128 kB
HardwareCorrupted:     0 kB
AnonHugePages:   6912000 kB
HugePages_Total:       0
HugePages_Free:        0
HugePages_Rsvd:        0
HugePages_Surp:        0
Hugepagesize:       2048 kB
DirectMap4k:        8184 kB
DirectMap2M:     8380416 kB 

1.2 top

Cpu(s)(cpu使用情況彙總): 1.0%us, 1.7%sy, 0.0%ni, 97.3%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st
Mem(內存使用情況彙總): 8059828k total, 7745016k used, 314812k free, 4072k buffers
Swap(swap使用情況彙總): 2096440k total, 1857260k used, 239180k free, 117384k cached

top - 18:55:25 up 25 days,  2:22,  1 user,  load average: 0.00, 0.02, 0.07
Tasks: 236 total,   2 running, 232 sleeping,   0 stopped,   2 zombie
Cpu(s):  1.0%us,  1.7%sy,  0.0%ni, 97.3%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Mem:   8059828k total,  7745016k used,   314812k free,     4072k buffers
Swap:  2096440k total,  1857260k used,   239180k free,   117384k cached

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
 6598 sankuai   20   0 7397m 2.5g 5276 S  1.3 33.0  64:45.67 java
28399 sankuai   20   0 7347m 4.3g 5700 S  1.0 56.0  41:50.25 java
30750 sankuai   20   0  782m 9900 2316 S  0.7  0.1  98:04.46 sg_agent
17360 root      20   0  130m  364  196 S  0.3  0.0  21:09.67 mcollectived
    1 root      20   0 91564  808  584 S  0.0  0.0   0:01.08 init
    2 root      20   0     0    0    0 S  0.0  0.0   0:00.00 kthreadd
    3 root      RT   0     0    0    0 S  0.0  0.0   0:00.00 migration/0
    4 root      20   0     0    0    0 S  0.0  0.0   0:08.90 ksoftirqd/0
    5 root      RT   0     0    0    0 S  0.0  0.0   0:00.00 migration/0

1.3 jps

jps獲取當前所有的java進程。其一般是jvm調試的第一步。通過該命令可以找到你要調試的jvm進程的pid,從而可以獲取到jvm的進程信息和堆的使用情況等。

~ $ jps
1362
2532 Main
2630 Jps
1383 RemoteMavenServer

此處jvm進程號爲2532 Main。可以通過jps -v可以查看java進程啓動時參數。 如此處的jvm啓動參數:-Xms1000m -XX:PermSize=256m -Dcore.zookeeper=sgconfig-zk.sankuai.com:9331

2532 Main -agentlib:jdwp=transport=dt_socket,address=127.0.0.1:56265,suspend=y,server=n -DSTOP.PORT=0 -Dcom.sun.management.jmxremote= -Dcom.sun.management.jmxremote.port=1099 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -DOPTIONS=jmx -javaagent:/Users/wenyi/Library/Caches/IntelliJIdea14/groovyHotSwap/gragent.jar -Xms1000m -XX:PermSize=256m -Dcore.zookeeper=sgconfig-zk.sankuai.com:9331 -Dfile.encoding=UTF-8 -Djetty.logs=/Users/wenyi/<directory>=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector -Djetty.port=9000 -Dmedis_environment=test -Dsun.jnu.encoding=UTF8 -Dignore_api_auth=true

2 java線程調試:jstack pid

jstack pid是用於打印jvm中線程的snapshot信息。其可以用於調試jvm中線程的數量和線程狀態等信息(每一個線程 frame ,包括類全名,方法名,代碼行),其在發現線程的死鎖、超時時特別有用。

java.lang.Thread.State :
RUNNABLE :表示當前線程處於可以運行的狀態,只要分配時間片就可以被調度執行。
WAITING(parking) :Waiting on condition等待鎖、等待網絡讀寫事件等的發生從而喚醒自己
WAITING (on object monitor):Object.wait( )[0x0000000133df9000] 其主要是指用戶調用了object.wait方法來實現線程間同步,其中只有一個線程持有鎖,並且保持在RUNNABLE狀態,其他線程處理WAITING狀態。
TIMED_WATTING(sleep 後會進入這種狀態 ) :只要定時時間到達,就將喚起該線程,使其變爲RUNNABLE
BLOCKED (線程阻塞,是指當前線程執行過程中,所需要的資源長時間等待卻一直未能獲取到,被容器的線程管理器標識爲阻塞狀態,可以理解爲等待資源超時的線程)如果是 BLOCKED 狀態就要注意了,看看 blocked 的狀態在等待什麼?因爲什麼而阻塞?

2016-11-25 23:29:53
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.77-b03 mixed mode):

"AWT-Shutdown" #886 prio=5 os_prio=31 tid=0x00007fbadadcf800 nid=0x656f in Object.wait() [0x0000000143449000]
   java.lang.Thread.State: TIMED_WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        at sun.awt.AWTAutoShutdown.run(AWTAutoShutdown.java:314)
        - locked <0x00000006c14901b0> (a java.lang.Object)
        at java.lang.Thread.run(Thread.java:745)

"elasticsearch[Titania][generic][T#13]" #885 daemon prio=5 os_prio=31 tid=0x00007fbad3af1000 nid=0x249d3 waiting on condition [0x0000000142a31000]
   java.lang.Thread.State: TIMED_WAITING (parking)
        at sun.misc.Unsafe.park(Native Method)
        - parking to wait for  <0x00000006c40c4b88> (a java.util.concurrent.SynchronousQueue$TransferStack)
        at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:215)
        at java.util.concurrent.SynchronousQueue$TransferStack.awaitFulfill(SynchronousQueue.java:460)
        at java.util.concurrent.SynchronousQueue$TransferStack.transfer(SynchronousQueue.java:362)
        at java.util.concurrent.SynchronousQueue.poll(SynchronousQueue.java:941)
        at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1066)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1127)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
        at java.lang.Thread.run(Thread.java:745)

"elasticsearch[Titania][generic][T#8]" #814 daemon prio=5 os_prio=31 tid=0x00007fbad1e02000 nid=0x16b23 waiting on condition [0x000000013a3af000]
   java.lang.Thread.State: TIMED_WAITING (parking)
        at sun.misc.Unsafe.park(Native Method)
        - parking to wait for  <0x00000006c40c4b88> (a java.util.concurrent.SynchronousQueue$TransferStack)
        at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:215)
        at java.util.concurrent.SynchronousQueue$TransferStack.awaitFulfill(SynchronousQueue.java:460)
        at java.util.concurrent.SynchronousQueue$TransferStack.transfer(SynchronousQueue.java:362)
        at java.util.concurrent.SynchronousQueue.poll(SynchronousQueue.java:941)
        at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1066)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1127)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
        at java.lang.Thread.run(Thread.java:745)

"repeat-relation-threadpool" #804 prio=5 os_prio=31 tid=0x00007fbab6c20000 nid=0x24a73 waiting on condition [0x0000000143ed3000]
   java.lang.Thread.State: WAITING (parking)
        at sun.misc.Unsafe.park(Native Method)
        - parking to wait for  <0x00000006d14fd598> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
        at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
        at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
        at java.util.concurrent.ArrayBlockingQueue.take(ArrayBlockingQueue.java:403)
        at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1067)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1127)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
        at java.lang.Thread.run(Thread.java:745)

"MySQL Statement Cancellation Timer" #796 daemon prio=5 os_prio=31 tid=0x00007fbab6817000 nid=0x24613 in Object.wait() [0x0000000143ccd000]
   java.lang.Thread.State: TIMED_WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        at java.util.TimerThread.mainLoop(Timer.java:552)
        - locked <0x00000006d32a5888> (a java.util.TaskQueue)
        at java.util.TimerThread.run(Timer.java:505)

"Attach Listener" #791 daemon prio=9 os_prio=31 tid=0x00007fbadc8b5800 nid=0x24267 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

常見的線程狀態原因總結:

TIMED_WATTING:可能由於線程中調用了Thread.sleep(long)、thread.join(long)、object.wait(long)方法,當指定時間到達後線程狀態將有TIMED_WAITING轉化到RUNNABLE狀態。因此對於這樣狀態的線程可以不用過多的關心,一般都是用戶可預期的操作。
WAITING (on object monitor):主要是由於調用thread.join()或者object.wait()方法從而進入等待狀態,如果是thread.join(),則目標線程執行完畢後會喚醒該線程變爲RUNNABLE;如果是因爲object.wait()方法進入等待的話,在鎖對象執行object.notify()或者object.notifyAll()之後會回到runnable狀態
WAITING (parking waiting on condition):該類線程狀態主要是由於線程在等待某個事件的發生,如果網絡請求的返回。如果出現很多該類狀態的線程其表示有大量的線程正等待着網絡讀寫,這可能是一個網絡瓶頸的徵兆。因爲網絡阻塞導致線程無法執行。因此這個狀態是排查一個網絡超時是否由於網絡性能的原因的關鍵狀態。
BLOCKED:是表示一個線程獲取鎖超時,其是死鎖的關鍵標誌,如果發現有多個線程處於BLOCKED狀態,那麼接下來需要排查的就是其waiting to lock的對象, 如果發現lock該對象的線程有在waiting to lock另外一個對象,並且這幾個線程都是處於BLOCKED狀態,那麼這就是一個明顯的死鎖狀態。
如下:是一個正常的lock狀態,其中一個線程waiting to lock <0x00000006c03aa668>而處於BLOCKED狀態,另外一個lock to <0x00000006c03aa668>的線程處於RUNNABLE狀態,這正是一個常見的互斥鎖的使用,每次只有一個線程佔有鎖,而執行臨界區的代碼。
對於如何快速的查找這樣的死鎖問題可以通過:jstack pid | grep ‘0x00000006c03aa668’ 這樣的命令來快速發現線程之間的鎖定情況從而排查死鎖問題

qtp130668770-27 Acceptor1 SelectChannelConnector@0.0.0.0:9000" #27 prio=5 os_prio=31 tid=0x00007fbad33b9000 nid=0x7303 waiting for monitor entry [0x000000012dd31000]
   java.lang.Thread.State: BLOCKED (on object monitor)
    at sun.nio.ch.ServerSocketChannelImpl.accept(ServerSocketChannelImpl.java:234)
    - **waiting to lock <0x00000006c03aa668>** (a java.lang.Object)
    at org.eclipse.jetty.server.nio.SelectChannelConnector.accept(SelectChannelConnector.java:109)
    at org.eclipse.jetty.server.AbstractConnector$Acceptor.run(AbstractConnector.java:938)
    at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:608)
    at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:543)
    at java.lang.Thread.run(Thread.java:745)


"qtp130668770-26 Acceptor0 [email protected]:9000" #26 prio=5 os_prio=31 tid=0x00007fbad41cb000 nid=0x7103 runnable [0x000000012dc2e000]
   java.lang.Thread.State: RUNNABLE
    at sun.nio.ch.ServerSocketChannelImpl.accept0(Native Method)
    at sun.nio.ch.ServerSocketChannelImpl.accept(ServerSocketChannelImpl.java:422)
    at sun.nio.ch.ServerSocketChannelImpl.accept(ServerSocketChannelImpl.java:250)
    - **locked <0x00000006c03aa668>** (a java.lang.Object)
    at org.eclipse.jetty.server.nio.SelectChannelConnector.accept(SelectChannelConnector.java:109)
    at org.eclipse.jetty.server.AbstractConnector$Acceptor.run(AbstractConnector.java:938)
    at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:608)
    at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:543)
    at java.lang.Thread.run(Thread.java:745)

DEAD: 當線程執行完畢,或者拋出了未捕獲的異常之後,會進入dead狀態,該線程結束。如果一個線程,特別是線程池中的線程處於DEAD狀態就應該特別小心了,看是否是線程出現異常而使得線程死亡。

自己遇到的幾個CASE的介紹
CASE 1 : WAITING (on object monitor)和Dead狀態的線程造成的死鎖。
這個是這段代碼的核心部分:

if(this.currentId.longValue() >= this.limit.longValue() && !this.fetchStatus.booleanValue()) {
                this.fetchStatus = Boolean.valueOf(true);
                this.fetchNewId();
            }

            while(this.currentId.longValue() >= this.curMaxId.longValue()) {
                try {
                    if(this.curMaxId.longValue() < this.newMaxId.longValue()) {
                        Long tmp = new Long(this.curMaxId.longValue());
                        this.curMaxId = this.newMaxId;
                        this.newMaxId = tmp;
                        this.currentId = Long.valueOf(this.curMaxId.longValue() - this.generatorProperty.getOffset().longValue());
                    } else {
                        this.wait();
                    }
                } catch (InterruptedException var2) {
                    return this.generateId();
                }
            }

            Long var10000 = this.currentId;
            this.currentId = Long.valueOf(this.currentId.longValue() + 1L);
            return var10000;
  • 當currentId的值大於limit值的時候,會調整curMaxId的值,使得其不超過負載,並且fetchNewId是通過啓動一個新的線程來調整curMaxId的值,並且通過fetchStatus保證只會有一個線程用於調整curMaxId。
  • 當currentId的值增加到curMaxId的時候,該線程會調用wait()方法,使得線程等待分配線程調整curMaxId的值。

這段代碼的關鍵部分就是fetchNewId()必須要分配成功不能失敗。因爲只要出現異常,其沒有調整成功curMaxId的值,並且fetchStatus沒有設置成false狀態,那麼後續的調用就不會在進入到fetchNewId的調用部分,因此必然造成程序的死鎖,其一直等待在獲取新的id的任務上。

現在看下實現的fetchNewId()方法:其主要問題是在想着複用數據庫的connection的問題上,因爲每次啓動和關閉數據庫連接是有代價的,因此爲了提高效率就複用了以前的connection。但問題就在這裏,數據庫對一個connection的最大連接時長是有限制的,其連接超過2天就會曝出timeOut的異常。而由於程序中沒有捕獲這個異常就造成這個線程最終變爲dead狀態,而其他線程都處於wait狀態,因此就死鎖了。
我在通過jstack 查看我的線程池的時候發現,我固定分配的8個線程有7個處於wait狀態,而另外一個線程處於dead狀態,同時由於代碼中有鎖和wait代碼的程序段就是一個id生產器分配id的調用,因此快速定位到應該是fetchNewId的調用失敗,同時去grep 異常發現了一個數據庫timeout 的異常,因此很快的就解決這個問題。

private void fetchNewId() {
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                Long curId = dbManager.getIdAndUpdate();
                updateMaxId(curId);
            }
        });

    }

public Long getIdAndUpdate() {
        Long currentId = null;
        for (int i = 0; i < 10; ++i) {
            try {
                currentId = selectAndUpdate();
                if (currentId != null) {
                    break;
                }
            } catch (MySQLTransactionRollbackException me) {
                try {
                    conn.rollback();
                } catch (SQLException se) {
                    se.printStackTrace();
                }
                me.printStackTrace();
            } catch (SQLException e) {
                try {
                    conn.rollback();
                } catch (SQLException se) {
                    se.printStackTrace();
                }
                e.printStackTrace();
                throw new RuntimeException("getIdAndUpdate error [" + e.getMessage() + "]");
            }
        }

        if (currentId == null) {
            throw new RuntimeException("getIdAndUpdate error after retry 10 times");
        }

        return currentId;
    }

對於這個段代碼的處理其實比較簡單,因爲其有limit的限制,同時你可以自己設置每次分配的id的數量,因此並不需要考慮對於數據連接重啓的時間浪費,因此只需如下變更代碼即可:每次啓動一個connection並關閉,就不會出現異常了,並且應該捕獲異常,不讓程序失敗。

public Long getIdAndUpdate() {
        Long currentId = null;

        while (true) {
            try {
                connection = DriverManager.getConnection(generatorProperty.getUrl(),
                        generatorProperty.getUserName(), generatorProperty.getPassword());

                //關閉自動提交,開啓事務
                connection.setAutoCommit(false);
                connection.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ);

                currentId = selectAndUpdate();
                if (currentId != null) {
                    break;
                }
            } catch (Exception e) {
                LOGGER.error("[DBManager] [getIdAndUpdate error!! begin rollback!] [msg: {}]", e.getMessage());
                try {
                    connection.rollback();
                } catch (SQLException se) {
                    LOGGER.error("[DBManager] [rollback error!!][msg: {}]", se.getMessage());
                }
            } finally {
                try {
                    connection.close();
                } catch (SQLException e) {
                    LOGGER.error("[DBManager] [close jdbc connection error!!][msg: {}]", e.getMessage());
                }
            }

        }
//        LOGGER.info("[DBManager] [get id:{}]", currentId);
        return currentId;
    }

case2: WAITING (parking) waiting on condition 等待某個條件發生造成的死鎖
代碼如下,下面這段程序99%都會造成死鎖。

package com.cweeyii.deadlock;

import java.util.Locale;
import java.util.concurrent.*;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Created by wenyi on 16/11/26.
 * Email:[email protected]
 */
public class DeadLock {
    private Lock firstLock=new ReentrantLock();
    private Lock secondLock=new ReentrantLock();

    public void method1() throws InterruptedException {
        firstLock.lock();
        Thread.sleep(1000);
        secondLock.lock();
        System.out.println("method1 be called");
        secondLock.unlock();
        firstLock.unlock();
    }

    public void method2() throws InterruptedException {
        secondLock.lock();
        Thread.sleep(1000);
        firstLock.lock();
        System.out.println("method1 be called");
        firstLock.unlock();
        secondLock.unlock();
    }

    public static void main(String[] args) throws InterruptedException {
        ExecutorService executorService=new ThreadPoolExecutor(
                4, 4, 30, TimeUnit.SECONDS,
                new ArrayBlockingQueue<Runnable>(10), new ThreadFactory() {
            @Override
            public Thread newThread(Runnable r) {
                final SecurityManager s = System.getSecurityManager();
                ThreadGroup group = (s != null) ? s.getThreadGroup() : Thread.currentThread()
                        .getThreadGroup();
                final Thread t = new Thread(group, r, String.format(Locale.ROOT, "%s",
                        "deadlock-pool"), 0);
                t.setDaemon(false);
                t.setPriority(Thread.NORM_PRIORITY);
                return t;
            }
        }, new ThreadPoolExecutor.CallerRunsPolicy());

        final DeadLock deadLock=new DeadLock();
        executorService.submit(new Runnable() {
            @Override
            public void run() {
                try {
                    deadLock.method1();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        executorService.submit(new Runnable() {
            @Override
            public void run() {
                try {
                    deadLock.method2();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        executorService.shutdown();
        while(!executorService.isTerminated()){
            System.out.println("線程池中還有任務在進行....");
            Thread.sleep(10000);
        }
        System.out.println("線程池中任務已經完成");
    }
}

jstack結果如下:

2016-11-26 17:22:52
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.77-b03 mixed mode):

"Attach Listener" #13 daemon prio=9 os_prio=31 tid=0x00007fda4a00f000 nid=0x380b waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"deadlock-pool" #12 prio=5 os_prio=31 tid=0x00007fda4d221800 nid=0x5903 waiting on condition [0x0000000122d1b000]
   java.lang.Thread.State: WAITING (parking)
    at sun.misc.Unsafe.park(Native Method)
    - parking to wait for  <0x000000076af99278> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
    at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:870)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1199)
    at java.util.concurrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLock.java:209)
    at java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:285)
    at com.cweeyii.deadlock.DeadLock.method2(DeadLock.java:28)
    at com.cweeyii.deadlock.DeadLock$3.run(DeadLock.java:67)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)

"deadlock-pool" #11 prio=5 os_prio=31 tid=0x00007fda4d20d000 nid=0x5703 waiting on condition [0x0000000122c18000]
   java.lang.Thread.State: WAITING (parking)
    at sun.misc.Unsafe.park(Native Method)
    - parking to wait for  <0x000000076af992a8> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
    at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:870)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1199)
    at java.util.concurrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLock.java:209)
    at java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:285)
    at com.cweeyii.deadlock.DeadLock.method1(DeadLock.java:19)
    at com.cweeyii.deadlock.DeadLock$2.run(DeadLock.java:56)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)

"Monitor Ctrl-Break" #10 daemon prio=5 os_prio=31 tid=0x00007fda4c113000 nid=0x5503 runnable [0x0000000122ab6000]
   java.lang.Thread.State: RUNNABLE
    at java.net.PlainSocketImpl.socketAccept(Native Method)
    at java.net.AbstractPlainSocketImpl.accept(AbstractPlainSocketImpl.java:409)
    at java.net.ServerSocket.implAccept(ServerSocket.java:545)
    at java.net.ServerSocket.accept(ServerSocket.java:513)
    at com.intellij.rt.execution.application.AppMain$1.run(AppMain.java:90)
    at java.lang.Thread.run(Thread.java:745)

"Service Thread" #9 daemon prio=9 os_prio=31 tid=0x00007fda49829000 nid=0x5103 runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C1 CompilerThread3" #8 daemon prio=9 os_prio=31 tid=0x00007fda49828800 nid=0x4f03 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread2" #7 daemon prio=9 os_prio=31 tid=0x00007fda49827800 nid=0x4d03 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread1" #6 daemon prio=9 os_prio=31 tid=0x00007fda49827000 nid=0x4b03 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"C2 CompilerThread0" #5 daemon prio=9 os_prio=31 tid=0x00007fda4c83e800 nid=0x4903 waiting on condition [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Signal Dispatcher" #4 daemon prio=9 os_prio=31 tid=0x00007fda4c82b800 nid=0x3c0f runnable [0x0000000000000000]
   java.lang.Thread.State: RUNNABLE

"Finalizer" #3 daemon prio=8 os_prio=31 tid=0x00007fda4c837000 nid=0x3503 in Object.wait() [0x000000012003e000]
   java.lang.Thread.State: WAITING (on object monitor)
    at java.lang.Object.wait(Native Method)
    - waiting on <0x000000076ab08ee0> (a java.lang.ref.ReferenceQueue$Lock)
    at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:143)
    - locked <0x000000076ab08ee0> (a java.lang.ref.ReferenceQueue$Lock)
    at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:164)
    at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:209)

"Reference Handler" #2 daemon prio=10 os_prio=31 tid=0x00007fda4c001800 nid=0x3303 in Object.wait() [0x000000011ff3b000]
   java.lang.Thread.State: WAITING (on object monitor)
    at java.lang.Object.wait(Native Method)
    - waiting on <0x000000076ab06b50> (a java.lang.ref.Reference$Lock)
    at java.lang.Object.wait(Object.java:502)
    at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
    - locked <0x000000076ab06b50> (a java.lang.ref.Reference$Lock)
    at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)

"main" #1 prio=5 os_prio=31 tid=0x00007fda4b801800 nid=0x1303 waiting on condition [0x000000010448e000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
    at java.lang.Thread.sleep(Native Method)
    at com.cweeyii.deadlock.DeadLock.main(DeadLock.java:76)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)

"VM Thread" os_prio=31 tid=0x00007fda4d803000 nid=0x3103 runnable

"GC task thread#0 (ParallelGC)" os_prio=31 tid=0x00007fda4a011000 nid=0x2103 runnable

"GC task thread#1 (ParallelGC)" os_prio=31 tid=0x00007fda4c000000 nid=0x2303 runnable

"GC task thread#2 (ParallelGC)" os_prio=31 tid=0x00007fda4b001000 nid=0x2503 runnable

"GC task thread#3 (ParallelGC)" os_prio=31 tid=0x00007fda4b002000 nid=0x2703 runnable

"GC task thread#4 (ParallelGC)" os_prio=31 tid=0x00007fda4b002800 nid=0x2903 runnable

"GC task thread#5 (ParallelGC)" os_prio=31 tid=0x00007fda4b003000 nid=0x2b03 runnable

"GC task thread#6 (ParallelGC)" os_prio=31 tid=0x00007fda4c001000 nid=0x2d03 runnable

"GC task thread#7 (ParallelGC)" os_prio=31 tid=0x00007fda4b80a800 nid=0x2f03 runnable

"VM Periodic Task Thread" os_prio=31 tid=0x00007fda4983a000 nid=0x5303 waiting on condition

JNI global references: 21


Found one Java-level deadlock:
=============================
"deadlock-pool":
  waiting for ownable synchronizer 0x000000076af99278, (a java.util.concurrent.locks.ReentrantLock$NonfairSync),
  which is held by "deadlock-pool"
"deadlock-pool":
  waiting for ownable synchronizer 0x000000076af992a8, (a java.util.concurrent.locks.ReentrantLock$NonfairSync),
  which is held by "deadlock-pool"

Java stack information for the threads listed above:
===================================================
"deadlock-pool":
    at sun.misc.Unsafe.park(Native Method)
    - parking to wait for  <0x000000076af99278> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
    at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:870)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1199)
    at java.util.concurrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLock.java:209)
    at java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:285)
    at com.cweeyii.deadlock.DeadLock.method2(DeadLock.java:28)
    at com.cweeyii.deadlock.DeadLock$3.run(DeadLock.java:67)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)
"deadlock-pool":
    at sun.misc.Unsafe.park(Native Method)
    - parking to wait for  <0x000000076af992a8> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
    at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:870)
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1199)
    at java.util.concurrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLock.java:209)
    at java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:285)
    at com.cweeyii.deadlock.DeadLock.method1(DeadLock.java:19)
    at com.cweeyii.deadlock.DeadLock$2.run(DeadLock.java:56)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)

**Found 1 deadlock.**

注意黑體部分,jstack會將死鎖的線程信息單獨打印出來。可以發現兩個線程在等待不同的object。其接下來就是要看是否是線程在相互等待這個各自加鎖的對象。
parking to wait for <0x000000076af99278>
parking to wait for <0x000000076af992a8>

CASE 3: BLOCKED (on object monitor)造成的死鎖,其和CASE 2 大同小異就是將lock緩存synchronized,但是使得死鎖更加明顯。

public class DeadLock {
    private Object firstObject=new Object();
    private Object secondObject=new Object();

    public void method1() throws InterruptedException {
        synchronized (firstObject){
            Thread.sleep(1000);
            synchronized (secondObject){
                System.out.println("method1 be called");
            }
        }
    }

    public void method2() throws InterruptedException {
        synchronized (secondObject){
            Thread.sleep(1000);
            synchronized (firstObject){
                System.out.println("method1 be called");
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        ExecutorService executorService=new ThreadPoolExecutor(
                4, 4, 30, TimeUnit.SECONDS,
                new ArrayBlockingQueue<Runnable>(10), new ThreadFactory() {
            @Override
            public Thread newThread(Runnable r) {
                final SecurityManager s = System.getSecurityManager();
                ThreadGroup group = (s != null) ? s.getThreadGroup() : Thread.currentThread()
                        .getThreadGroup();
                final Thread t = new Thread(group, r, String.format(Locale.ROOT, "%s",
                        "deadlock-pool"), 0);
                t.setDaemon(false);
                t.setPriority(Thread.NORM_PRIORITY);
                return t;
            }
        }, new ThreadPoolExecutor.CallerRunsPolicy());

        final DeadLock deadLock=new DeadLock();
        executorService.submit(new Runnable() {
            @Override
            public void run() {
                try {
                    deadLock.method1();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        executorService.submit(new Runnable() {
            @Override
            public void run() {
                try {
                    deadLock.method2();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        executorService.shutdown();
        while(!executorService.isTerminated()){
            System.out.println("線程池中還有任務在進行....");
            Thread.sleep(10000);
        }
        System.out.println("線程池中任務已經完成");
    }
}
"deadlock-pool" #12 prio=5 os_prio=31 tid=0x00007fa2ec883800 nid=0x5903 waiting for monitor entry [0x0000000128b9c000]
   java.lang.Thread.State: BLOCKED (on object monitor)
    at com.cweeyii.deadlock.DeadLock.method2(DeadLock.java:29)
    - waiting to lock <0x000000076af98e40> (a java.lang.Object)
    - locked <0x000000076af98e50> (a java.lang.Object)
    at com.cweeyii.deadlock.DeadLock$3.run(DeadLock.java:67)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)

"deadlock-pool" #11 prio=5 os_prio=31 tid=0x00007fa2ec9a5000 nid=0x5703 waiting for monitor entry [0x0000000128a99000]
   java.lang.Thread.State: BLOCKED (on object monitor)
    at com.cweeyii.deadlock.DeadLock.method1(DeadLock.java:20)
    - waiting to lock <0x000000076af98e50> (a java.lang.Object)
    - locked <0x000000076af98e40> (a java.lang.Object)
    at com.cweeyii.deadlock.DeadLock$2.run(DeadLock.java:56)
    at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)

線程1:
- waiting to lock <0x000000076af98e40> (a java.lang.Object)
- locked <0x000000076af98e50> (a java.lang.Object)

線程2:
- waiting to lock <0x000000076af98e50> (a java.lang.Object)
- locked <0x000000076af98e40> (a java.lang.Object)
可以發現兩個線程之間相互佔有對方需要的對象。

3 JVM內存

機器內存模型:

  • 硬件內存是用於存儲程序實際使用的數據的地方,其中每一個可以被程序使用的數據都映射到物理內存中的某個單元。
  • 虛擬內存是程序具體標識一個數據單元的地址,其包括指令的地址和數據地址。虛擬內存限制了程序最大使用的空間範圍:如32位機最多使用的內存範圍2^32=4G個地址,因此其最大可以使用4G個地址單元,每個被機器使用的虛擬地址都會映射到物理內存中的一個單元。那麼一個2G內存的機器是否可以表示4G虛擬地址的程序呢?答案是可以的,其主要是通過物理內存+swap(磁盤)實現的。如果一個程序佔用了4G的虛擬地址,並且都沒有被釋放。那麼當一個虛擬地址被讀取的時候,如果該虛擬地址映射到了一個物理內存,那麼這個數據可以直接使用,如果沒有映射到物理內存,那麼首先如果物理內存還有空餘那麼會直接分配一個存儲單元給該虛擬地址,如果物理內存都被映射,那麼會首先會將以前使用過的物理內存數據換出到swap磁盤中,並將已經換出的物理內存分配給該虛擬地址,從而達到超過4G地址的功能,當要重新使用以前的數據,會從swap中換入到物理內存中,因此當程序中存活的虛擬內存空間大於2G時就會進行頻繁的發生swap的換入換出,從而佔用cpu,使用得用戶感覺程序的卡頓。(其實際上一個進程的使用空間沒有達到4G,並且swap什麼時候進行換入換出可以程序指定如佔比80%的物理內存時)。因此通過虛擬內存技術即虛擬內存地址與物理內存的映射+物理內存與swap磁盤的換人換出技術,從而達到無線擴容的目的。
  • Java進程(即JVM進程): java程序是以jvm進程形式存在於系統中,每一個java程序都會啓動一個jvm進程。jvm進程的內存空間包括:1)內核內存該類型同普通進程一樣保存linux系統相關的變量、代碼等,如文件句柄、scoket句柄等。2)永久代(代碼區和數據區):Java程序中類(class),會被加載到該區域的不同數據結構中去,包括常量池、域、方法數據、方法體、構造函數、以及類中的專用方法、實例初始化、接口初始化等。其是用於容納程序指令及class的類數據、常量等。 3)Java堆(年輕代+年老代):其主要用於保存java程序中的對象以及class對象。JVM向操作系統申請一整段內存區域(具體大小可以在JVM參數調節)作爲Java程序的堆(分爲新生代和老年代);當Java程序申請內存空間,比如執行new操作,JVM將在這段空間中按所需大小分配給Java程序,並分配具體的物理內存。因此具體使用的物理內存量和分配的Java對象佔用的比例相關和JVM啓動時設置的-Xmx -Xms -Xmn不一定有關(與具體jvm的設計方式有關)。JVM啓動時參數是限制可使用的最大和最小內存數量。但一般都會保證設置的jvm參數小於實際的內存大小,從而保證不會在分配內存的時候發生內存的swap操作而不調用full gc,影響程序性能。 4)java棧區:棧區是java線程用於保存臨時變量、回調地址等變量的地方,默認每個線程的java棧大小是1M. 因此棧區所佔大小是同線程的數量成正比 5)未分配區域:未使用區是分配新內存空間的預備區域。JVM進程來說,調整堆大小及線程棧時會使用該區域,而堆大小一般較少調整,因此大小相對穩定。操作系統會動態調整這個區域的大小,並且這個區域通常並沒有被分配實際的物理內存,只是允許進程在這個區域申請堆或棧空間。

    內存模型

3.1 jvm垃圾收集策略

jvm垃圾收集,其主要是區分出垃圾對象和在使用的對象,然後回收垃圾對象所佔用的內存。現存的jvm垃圾收集算法都是分代進行收集的,因爲80%以上的對象的生命週期都是十分短暫的,只有20%的對象的生命週期較長(二八定律)。因此按照對象的生命週期長短的進行分代,然後在不同分代進行不同的算法或者不同頻率進行垃圾收集比在整個內存空間進行垃圾清洗能夠帶來更大的性能提升(更大的吞吐量、更短的垃圾收集時間)。
jvm分代內存模型
通過把堆內存空間分爲:年輕代+ 年老代
年老代(進行Minor GC算法) 年老代(進行Full GC算法) 另外由於永久代同年老代一樣其中的佔用內存的對象(永久代存儲的類的方法數據等)生命週期都很長(一般只有這個類不再被使用時,才從永久代中釋放空間),所以也同年老代一樣進行full gc。

1.垃圾內存收集算法–複製拷貝:該算法的思想是將可用內存按容量劃分爲大小相等的兩塊,每次只使用其中的一塊。當這塊的內存用完了,則將還存活的對象複製到另外一塊內存上去,然後再把剛使用過的內存空間一次清理掉。從而達到了每次垃圾回收時只是針對其中一塊,避免了產生內存碎片等情況。
複製拷貝
回收前狀態其右邊內存空間是被保留不能使用的,當運行垃圾收集算法時,其將左側的存活對象拷貝到右邊。當算法運行結束正在使用的內存空間變爲了右側,左側變爲保留區域。
複製拷貝算法的特點:1)每次進行垃圾收集算法後,存活對象都是存在於緊鄰的內存區域,從而解決了內存碎片的問題 2)複製拷貝算法的執行時間和存活對象的數量正正比,因此該算法是用於存活對象較少的情形。例如:minor gc

這裏寫圖片描述
年輕代的複製拷貝垃圾收集算法(minor gc):其爲了最大限度的利用內存空間並不是把年輕代按照1:1分爲使用區域和保留區域(因爲分代模型保證了年輕代中長生命週期的對象較少)。其實際上是分爲3個區域,Eden區+Survivor區(8:2,其值可以通過參數-XX:SurvivorRatio設置,默認爲8), 其中Survivor區按照1:1的比例分爲From區+To區。
在使用過程中Eden區+From區是可以用於分配內存的,而TO區域是保留的。在每次minor gc進行後from區和to區交換位置。在minor gc進行時,Eden區存活的對象會都別拷貝到To區,From區對象根據gc次數來判斷是應該進入To區還是老年代,如果From區對象gc次數小於閾值(通過-XX:MaxTenuringThreshold來設置,默認15)則進入To區,如果gc次數大於閾值則進入到年老代。在一次minor gc後From和To區交換名字。新的Eden區和From區都是未使用狀態,可以用於分配對象。如果To區被填滿,那麼To區所有對象被拷貝到年老代,從而可能引起Full gc。-XX:MaxTenuringThreshold用於控制對象在年輕代中存活的時間,其值越大其在年輕代中存活的時間越長,其被垃圾收集的概率越大,如果設置爲0,那麼在minor gc時,Eden區和From區存活的對象直接進入年老代,而不經過To區。複製拷貝算法適用於年輕代,這種算法在對象存活率較高時就要進行較多的複製操作,效率將變低,因此不適用於年老代,年老代一般採用的算法是標記-清除算法。

2.垃圾內存收集算法–標記-清除:標記的過程其實就是,遍歷所有的GC Roots,然後將所有GC Roots可達的對象標記爲存活的對象;清除的過程將遍歷堆中所有的對象,將沒有標記的對象全部清除掉。同時,由於標記-清除只會將沒有標記的對象清除掉不會移動對象,在一段時間後會造成內存碎片的問題,因此需要進行對象的壓縮將內存中的存活對象移動到內存的一端,如清除整理算法的處理過程一樣。
標記清除

3.垃圾內存收集算法–標記-整理:標記整理算法與標記清除算法是在釋放內存空間的步驟,其是讓所有存活的對象都向一端移動,然後直接清理掉端邊界以外的內存。因此在存活對象較多時標記-整理算法要遠慢於標記清除算法。
標記整理算法

年老代常用的垃圾收集算法是併發標記清除算法(-XX:+UseConcMarkSweepGC)其通過設置(-XX:CMSFullGCsBeforeCompaction)設置在多少次full gc之後,對內存進行壓縮,從而清除內存碎片。

因此常用的jvm分代垃圾收集算法是:年輕代使用-XX:+UseParNewGC(複製拷貝) 年老代-XX:+UseConcMarkSweepGC(標記清除和標記整理)

CUSTOM_JVM = -Xmx5g -Xms5g -Xmn3g -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=3 -server -XX:PermSize=128m -XX:MaxPermSize=256m -XX:+UseParNewGC -XX:ParallelGCThreads=8 -XX:+UseAdaptiveSizePolicy -XX:MaxGCPauseMillis=100 -XX:+UseConcMarkSweepGC -XX:+PrintCommandLineFlags -XX:+UseConcMarkSweepGC -XX:CMSFullGCsBeforeCompaction=0 -XX:+UseCMSCompactAtFullCollection -XX:CMSInitiatingOccupancyFraction=68 -XX:+HeapDumpOnOutOfMemoryError -XX:+PrintGCDetails

堆內存(年輕代+年老代)分配方案1:-Xmx5g -Xms5g -Xmn3g -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=3
-Xmx5g:設置JVM最大可用內存爲5g
-Xms5g : 設置JVM初始內存爲5g。應該設置與-Xmx相同,以避免JVM動態調整堆內存大小影響程序性能。
-Xmn2g:設置年輕代大小爲2G, 年老代=Xmx5g-Xmn2g=3g。增大年輕代後,將會減小年老代大小。此值對系統性能影響較大,Sun官方推薦配置爲整個堆的3/8。
-XX:SurvivorRatio=8: 設置年輕代中Eden區與Survivor區的大小比值,默認爲8,即Eden區佔年輕代的8/10,一個Survivor區佔整個年輕代的1/10。
-XX:MaxTenuringThreshold=3:設置年輕代的對象經過3次minor gc後會進入年老代。

堆內存分配方案2:-Xmx5g -Xms5g -XX:NewRatio=2 -XX:SurvivorRatio=8 -XX:MaxTenuringThreshold=3
-XX:NewRatio:設置年老代與年輕代的比值。設置爲2,則年輕代與年老代所佔比值爲1:2,年輕代佔整個堆棧的1/3。Xms=Xmx並且設置了Xmn的情況下,該參數無效。

持久代內存設置:-XX:PermSize=128m -XX:MaxPermSize=256m
-XX:PermSize:設置持久代的初始大小爲128m
-XX:MaxPermSize=256m: 設置持久代的最大內存大小爲256m.
持久代內存不用太大,因爲其主要是保存要使用的類的方法等數據信息,其fullgc的頻率較小,並且釋放的內存也較少。

垃圾算法的設置(年輕代):-XX:+UseParNewGC -XX:ParallelGCThreads=8 -XX:MaxGCPauseMillis=100 -XX:+UseAdaptiveSizePolicy
-XX:+UseParNewGC:設置年輕代的垃圾算法爲並行收集算法(多線程複製拷貝)
-XX:ParallelGCThreads:設置並行垃圾收集算法的線程數,最好設置爲核心數。
-XX:MaxGCPauseMillis=100:設置minor gc最大的暫停時間,如果不滿足該時間會調整年輕代內存大小
-XX:+UseAdaptiveSizePolicy:自動選擇年輕代區大小和相應的Survivor區比例,設置該值後jvm會調整年輕代Eden和Survivor比例來滿足暫停時間、收集頻率等的要求,建議都開啓

垃圾算法的設置(年老代)
-XX:+UseConcMarkSweepGC:設置年老代爲CMS併發算法(標記清除和標記整理)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章