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/=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(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(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进程来说,调整堆大小及线程栈时会使用该区域,而堆大小一般较少调整,因此大小相对稳定。操作系统会动态调整这个区域的大小,并且这个区域通常并没有被分配实际的物理内存,只是允许进程在这个区域申请堆或栈空间。

    JVM分析工具_第1张图片

3.1 jvm垃圾收集策略

jvm垃圾收集,其主要是区分出垃圾对象和在使用的对象,然后回收垃圾对象所占用的内存。现存的jvm垃圾收集算法都是分代进行收集的,因为80%以上的对象的生命周期都是十分短暂的,只有20%的对象的生命周期较长(二八定律)。因此按照对象的生命周期长短的进行分代,然后在不同分代进行不同的算法或者不同频率进行垃圾收集比在整个内存空间进行垃圾清洗能够带来更大的性能提升(更大的吞吐量、更短的垃圾收集时间)。
JVM分析工具_第2张图片
通过把堆内存空间分为:年轻代+ 年老代
年老代(进行Minor GC算法) 年老代(进行Full GC算法) 另外由于永久代同年老代一样其中的占用内存的对象(永久代存储的类的方法数据等)生命周期都很长(一般只有这个类不再被使用时,才从永久代中释放空间),所以也同年老代一样进行full gc。

1.垃圾内存收集算法–复制拷贝:该算法的思想是将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这块的内存用完了,则将还存活的对象复制到另外一块内存上去,然后再把刚使用过的内存空间一次清理掉。从而达到了每次垃圾回收时只是针对其中一块,避免了产生内存碎片等情况。
JVM分析工具_第3张图片
回收前状态其右边内存空间是被保留不能使用的,当运行垃圾收集算法时,其将左侧的存活对象拷贝到右边。当算法运行结束正在使用的内存空间变为了右侧,左侧变为保留区域。
复制拷贝算法的特点:1)每次进行垃圾收集算法后,存活对象都是存在于紧邻的内存区域,从而解决了内存碎片的问题 2)复制拷贝算法的执行时间和存活对象的数量正正比,因此该算法是用于存活对象较少的情形。例如:minor gc

JVM分析工具_第4张图片
年轻代的复制拷贝垃圾收集算法(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可达的对象标记为存活的对象;清除的过程将遍历堆中所有的对象,将没有标记的对象全部清除掉。同时,由于标记-清除只会将没有标记的对象清除掉不会移动对象,在一段时间后会造成内存碎片的问题,因此需要进行对象的压缩将内存中的存活对象移动到内存的一端,如清除整理算法的处理过程一样。
JVM分析工具_第5张图片

3.垃圾内存收集算法–标记-整理:标记整理算法与标记清除算法是在释放内存空间的步骤,其是让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。因此在存活对象较多时标记-整理算法要远慢于标记清除算法。
JVM分析工具_第6张图片

年老代常用的垃圾收集算法是并发标记清除算法(-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并发算法(标记清除和标记整理)

你可能感兴趣的:(JVM调试)