JUC多线程并发、JVM和GC

Callable接口

Runnable和Callable的区别

  • Runnable是自从java1.1就有了,而Callable是1.5之后才加上去的
  • Callable规定的方法是call(),Runnable规定的方法是run()
  • Callable的任务执行后可返回值,而Runnable的任务是不能返回值(是void)
  • call方法可以抛出异常,run方法不可以
  • 运行Callable任务可以拿到一个Future对象,表示异步计算的结果。它提供了检查计算是否完成的方法,以等待计算的完成,并检索计算的结果。通过Future对象可以了解任务执行情况,可取消任务的执行,还可获取执行结果。
  • 加入线程池运行,Runnable使用ExecutorService的execute方法,Callable使用submit方法。

Callable接口

callable有个<V>,这个V就是call函数的返回值类型

package java.util.concurrent;

/**
 * A task that returns a result and may throw an exception.
 * Implementors define a single method with no arguments called
 * {@code call}.
 *
 * <p>The {@code Callable} interface is similar to {@link
 * java.lang.Runnable}, in that both are designed for classes whose
 * instances are potentially executed by another thread.  A
 * {@code Runnable}, however, does not return a result and cannot
 * throw a checked exception.
 *
 * <p>The {@link Executors} class contains utility methods to
 * convert from other common forms to {@code Callable} classes.
 *
 * @see Executor
 * @since 1.5
 * @author Doug Lea
 * @param <V> the result type of method {@code call}
 */
@FunctionalInterface
public interface Callable<V> {
    /**
     * Computes a result, or throws an exception if unable to do so.
     *
     * @return computed result
     * @throws Exception if unable to compute a result
     */
    V call() throws Exception;
}

FutureTask类

/**
 * A cancellable asynchronous computation.  This class provides a base
 * implementation of {@link Future}, with methods to start and cancel
 * a computation, query to see if the computation is complete, and
 * retrieve the result of the computation.  The result can only be
 * retrieved when the computation has completed; the {@code get}
 * methods will block if the computation has not yet completed.  Once
 * the computation has completed, the computation cannot be restarted
 * or cancelled (unless the computation is invoked using
 * {@link #runAndReset}).
 *
 * <p>A {@code FutureTask} can be used to wrap a {@link Callable} or
 * {@link Runnable} object.  Because {@code FutureTask} implements
 * {@code Runnable}, a {@code FutureTask} can be submitted to an
 * {@link Executor} for execution.
 *
 * <p>In addition to serving as a standalone class, this class provides
 * {@code protected} functionality that may be useful when creating
 * customized task classes.
 *
 * @since 1.5
 * @author Doug Lea
 * @param <V> The result type returned by this FutureTask's {@code get} methods
 */
public class FutureTask<V> implements RunnableFuture<V> 
/**
 * Creates a {@code FutureTask} that will, upon running, execute the
 * given {@code Callable}.
 *
 * @param  callable the callable task
 * @throws NullPointerException if the callable is null
 */
public FutureTask(Callable callable) {
    if (callable == null)
        throw new NullPointerException();
    this.callable = callable;
    this.state = NEW;       // ensure visibility of callable
}

RunnableFuture接口

package java.util.concurrent;

/**
 * A {@link Future} that is {@link Runnable}. Successful execution of
 * the {@code run} method causes completion of the {@code Future}
 * and allows access to its results.
 * @see FutureTask
 * @see Executor
 * @since 1.6
 * @author Doug Lea
 * @param <V> The result type returned by this Future's {@code get} method
 */
public interface RunnableFuture<V> extends Runnable, Future<V> {
    /**
     * Sets this Future to the result of its computation
     * unless it has been cancelled.
     */
    void run();
}

测试

public class CallableTest {
    public static void main(String[] args) throws Exception {

        MyThread mt = new MyThread();

        FutureTask<Integer> result = new FutureTask<Integer>(mt);

        new Thread(result).start();

        // 获取运算结果是同步过程,即 call 方法执行完成,才能获取结果
        Integer sum = result.get();

        System.out.println(sum);
    }
}
class MyThread implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        int sum = 0;

        for (int i = 1; i <= 100; i++) {
            sum += i;
        }

        return sum;
    }

}

当某个请求需要在后端完成 N 次统计结果时,我们就可以使用该方式创建 N 个线程进行(并行)统计,而不需要同步等待其他统计操作完成后才统计另一个结果。

线程池

线程池3个常用方式

Executors.newFixedThreadPool


/**
 * Creates a thread pool that reuses a fixed number of threads
 * operating off a shared unbounded queue.  At any point, at most
 * {@code nThreads} threads will be active processing tasks.
 * If additional tasks are submitted when all threads are active,
 * they will wait in the queue until a thread is available.
 * If any thread terminates due to a failure during execution
 * prior to shutdown, a new one will take its place if needed to
 * execute subsequent tasks.  The threads in the pool will exist
 * until it is explicitly {@link ExecutorService#shutdown shutdown}.
 *
 * @param nThreads the number of threads in the pool
 * @return the newly created thread pool
 * @throws IllegalArgumentException if {@code nThreads <= 0}
 */
public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}

Executors.newSingleThreadExecutor

/**
 * Creates an Executor that uses a single worker thread operating
 * off an unbounded queue. (Note however that if this single
 * thread terminates due to a failure during execution prior to
 * shutdown, a new one will take its place if needed to execute
 * subsequent tasks.)  Tasks are guaranteed to execute
 * sequentially, and no more than one task will be active at any
 * given time. Unlike the otherwise equivalent
 * {@code newFixedThreadPool(1)} the returned executor is
 * guaranteed not to be reconfigurable to use additional threads.
 *
 * @return the newly created single-threaded Executor
 */
public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}

Executors.newCachedThreadPool

/**
 * Creates a thread pool that creates new threads as needed, but
 * will reuse previously constructed threads when they are
 * available.  These pools will typically improve the performance
 * of programs that execute many short-lived asynchronous tasks.
 * Calls to {@code execute} will reuse previously constructed
 * threads if available. If no existing thread is available, a new
 * thread will be created and added to the pool. Threads that have
 * not been used for sixty seconds are terminated and removed from
 * the cache. Thus, a pool that remains idle for long enough will
 * not consume any resources. Note that pools with similar
 * properties but different details (for example, timeout parameters)
 * may be created using {@link ThreadPoolExecutor} constructors.
 *
 * @return the newly created thread pool
 */
public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}

线程池7大参数

Java 多线程开发之 Callable 与线程池(三) - 后端 - 掘金

  • int corePoolSize 线程池核心线程个数,默认线程池线程个数为 0,只有接到任务才新建线程

  • int maximumPoolSize 线程池最大线程数量

  • long keepAliveTime 线程池空闲时,线程存活的时间,当线程池中的线程数大于 corePoolSize 时才会起作用

  • TimeUnit unit 时间单位

  • BlockingQueue workQueue 阻塞队列,当达到线程数达到 corePoolSize 时,将任务放入队列等待线程处理

  • ThreadFactory threadFactory 线程工厂

  • RejectedExecutionHandler handler 线程拒绝策略,当队列满了并且线程个数达到 maximumPoolSize 后采取的策略


/**
 * Creates a new {@code ThreadPoolExecutor} with the given initial
 * parameters and default thread factory.
 *
 * @param corePoolSize the number of threads to keep in the pool, even
 *        if they are idle, unless {@code allowCoreThreadTimeOut} is set
 * @param maximumPoolSize the maximum number of threads to allow in the
 *        pool
 * @param keepAliveTime when the number of threads is greater than
 *        the core, this is the maximum time that excess idle threads
 *        will wait for new tasks before terminating.
 * @param unit the time unit for the {@code keepAliveTime} argument
 * @param workQueue the queue to use for holding tasks before they are
 *        executed.  This queue will hold only the {@code Runnable}
 *        tasks submitted by the {@code execute} method.
 * @param handler the handler to use when execution is blocked
 *        because the thread bounds and queue capacities are reached
 * @throws IllegalArgumentException if one of the following holds:<br>
 *         {@code corePoolSize < 0}<br>
 *         {@code keepAliveTime < 0}<br>
 *         {@code maximumPoolSize <= 0}<br>
 *         {@code maximumPoolSize < corePoolSize}
 * @throws NullPointerException if {@code workQueue}
 *         or {@code handler} is null
 */
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          RejectedExecutionHandler handler) {
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
         Executors.defaultThreadFactory(), handler);
}

/**
 * Creates a new {@code ThreadPoolExecutor} with the given initial
 * parameters.
 *
 * @param corePoolSize the number of threads to keep in the pool, even
 *        if they are idle, unless {@code allowCoreThreadTimeOut} is set
 * @param maximumPoolSize the maximum number of threads to allow in the
 *        pool
 * @param keepAliveTime when the number of threads is greater than
 *        the core, this is the maximum time that excess idle threads
 *        will wait for new tasks before terminating.
 * @param unit the time unit for the {@code keepAliveTime} argument
 * @param workQueue the queue to use for holding tasks before they are
 *        executed.  This queue will hold only the {@code Runnable}
 *        tasks submitted by the {@code execute} method.
 * @param threadFactory the factory to use when the executor
 *        creates a new thread
 * @param handler the handler to use when execution is blocked
 *        because the thread bounds and queue capacities are reached
 * @throws IllegalArgumentException if one of the following holds:<br>
 *         {@code corePoolSize < 0}<br>
 *         {@code keepAliveTime < 0}<br>
 *         {@code maximumPoolSize <= 0}<br>
 *         {@code maximumPoolSize < corePoolSize}
 * @throws NullPointerException if {@code workQueue}
 *         or {@code threadFactory} or {@code handler} is null
 */
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler) {
    if (corePoolSize < 0 ||
        maximumPoolSize <= 0 ||
        maximumPoolSize < corePoolSize ||
        keepAliveTime < 0)
        throw new IllegalArgumentException();
    if (workQueue == null || threadFactory == null || handler == null)
        throw new NullPointerException();
    this.acc = System.getSecurityManager() == null ?
            null :
            AccessController.getContext();
    this.corePoolSize = corePoolSize;
    this.maximumPoolSize = maximumPoolSize;
    this.workQueue = workQueue;
    this.keepAliveTime = unit.toNanos(keepAliveTime);
    this.threadFactory = threadFactory;
    this.handler = handler;
}

线程池的合理配置

获取系统设备处理器核数

Runtime.getRuntime().availableProcessors()

考虑因素

CPU密集型

线程数 = CPU可用核心数/(1 - 阻塞系数),其中阻塞系数的取值在0和1之间

IO密集型

线程池的工作原理

1557299840009

execute方法执行逻辑

核心线程数

如果当前运行的线程少于corePoolSize,则会创建新的线程来执行新的任务

加入阻塞队列

如果运行的线程个数等于或者大于corePoolSize,则会将提交的任务存放到阻塞队列workQueue

扩容

如果当前workQueue队列已满的话,则会创建新的线程来执行任务

拒绝策略

如果线程个数已经超过了maximumPoolSize,则会使用饱和策略RejectedExecutionHandler来进行处理

JVM

常见故障

StackOverflowError

public class StackOverflowErrorDemo {
    public static void main(String[] args) {
        stackOverflowError();
    }

    private static void stackOverflowError() {
        stackOverflowError();
    }
}

结果

Exception in thread "main" java.lang.StackOverflowError
    at StackOverflowErrorDemo.stackOverflowError(StackOverflowErrorDemo.java:7)
    at StackOverflowErrorDemo.stackOverflowError(StackOverflowErrorDemo.java:7)
    at StackOverflowErrorDemo.stackOverflowError(StackOverflowErrorDemo.java:7)

Java heap space

import java.util.Random;

public class JavaHeapSpaceDemo {
    public static void main(String[] args) {

        String str = "abc";

        while (true){
            str+=str+new Random().nextInt(111111)+new Random().nextInt(222222);
            str.intern();
        }
    }
}

运行

配置参数

1564643060262

结果

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    at java.util.Arrays.copyOf(Arrays.java:3332)
    at java.lang.AbstractStringBuilder.ensureCapacityInternal(AbstractStringBuilder.java:124)
    at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:448)
    at java.lang.StringBuilder.append(StringBuilder.java:136)
    at JavaHeapSpaceDemo.main(JavaHeapSpaceDemo.java:9)

GC overhead limit exceeded

GC回收时间过长时会抛出OutOfMemoryError。过长的定义是,超过98%的时间用来做GC并且回收了不到2%的堆内存

连续多次GC都只回收了不到2%的极端情况下才会抛出。假如不抛出GC overhead limit 错误会发生什么情况呢?

那就是GC清理的这么点内存很快会再次填满,迫使GC再次致谢,这样就形成了恶性循环,CPU使用率一直是100%,而GC却没有任何成果

import java.util.ArrayList;
import java.util.List;

public class GCOverheadDemo {
    public static void main(String[] args) {

        int i = 0;
        List<String> list = new ArrayList<>();

        try {

            while (true) {
                list.add(String.valueOf(++i).intern());
            }

        } catch (Exception e) {
            System.out.println("**************i:" + i);
            e.printStackTrace();
            throw e;
        }

    }
}

运行参数配置

1564643600222

结果

[GC (Allocation Failure) [PSYoungGen: 2048K->480K(2560K)] 2048K->916K(9728K), 0.0043924 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] 
[GC (Allocation Failure) [PSYoungGen: 2528K->480K(2560K)] 2964K->2709K(9728K), 0.0022096 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 2528K->504K(2560K)] 4757K->4835K(9728K), 0.0028678 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (Allocation Failure) [PSYoungGen: 2552K->488K(2560K)] 6883K->6764K(9728K), 0.0032660 secs] [Times: user=0.06 sys=0.00, real=0.00 secs] 
[Full GC (Ergonomics) [PSYoungGen: 488K->0K(2560K)] [ParOldGen: 6276K->6188K(7168K)] 6764K->6188K(9728K), [Metaspace: 3225K->3225K(1056768K)], 0.0784031 secs] [Times: user=0.25 sys=0.00, real=0.08 secs] 
[Full GC (Ergonomics) [PSYoungGen: 2048K->738K(2560K)] [ParOldGen: 6188K->7039K(7168K)] 8236K->7777K(9728K), [Metaspace: 3225K->3225K(1056768K)], 0.0437521 secs] [Times: user=0.19 sys=0.00, real=0.04 secs] 
[Full GC (Ergonomics) [PSYoungGen: 2048K->2046K(2560K)] [ParOldGen: 7039K->7039K(7168K)] 9087K->9086K(9728K), [Metaspace: 3225K->3225K(1056768K)], 0.0405225 secs] [Times: user=0.28 sys=0.00, real=0.04 secs] 
[Full GC (Ergonomics) [PSYoungGen: 2048K->2047K(2560K)] [ParOldGen: 7039K->7039K(7168K)] 9087K->9087K(9728K), [Metaspace: 3225K->3225K(1056768K)], 0.0315932 secs] [Times: user=0.13 sys=0.00, real=0.03 secs] 
[Full GC (Ergonomics) [PSYoungGen: 2047K->2047K(2560K)] [ParOldGen: 7039K->7039K(7168K)] 9087K->9087K(9728K), [Metaspace: 3225K->3225K(1056768K)], 0.0350043 secs] [Times: user=0.23 sys=0.00, real=0.04 secs] 
[Full GC (Ergonomics) [PSYoungGen: 2047K->2047K(2560K)] [ParOldGen: 7041K->7041K(7168K)] 9089K->9089K(9728K), [Metaspace: 3225K->3225K(1056768K)], 0.0343159 secs] [Times: user=0.25 sys=0.00, real=0.04 secs] 
[Full GC (Ergonomics) [PSYoungGen: 2047K->2047K(2560K)] [ParOldGen: 7043K->7043K(7168K)] 9091K->9091K(9728K), [Metaspace: 3225K->3225K(1056768K)], 0.0335877 secs] [Times: user=0.33 sys=0.00, real=0.03 secs] 
[Full GC (Ergonomics) [PSYoungGen: 2047K->2047K(2560K)] [ParOldGen: 7044K->7044K(7168K)] 9092K->9092K(9728K), [Metaspace: 3225K->3225K(1056768K)], 0.0324352 secs] [Times: user=0.20 sys=0.00, real=0.03 secs] 
[Full GC (Ergonomics) [PSYoungGen: 2047K->2047K(2560K)] [ParOldGen: 7046K->7046K(7168K)] 9094K->9094K(9728K), [Metaspace: 3225K->3225K(1056768K)], 0.0329995 secs] [Times: user=0.20 sys=0.00, real=0.03 secs] 
[Full GC (Ergonomics) [PSYoungGen: 2047K->2047K(2560K)] [ParOldGen: 7048K->7048K(7168K)] 9096K->9096K(9728K), [Metaspace: 3225K->3225K(1056768K)], 0.0342704 secs] [Times: user=0.14 sys=0.00, real=0.03 secs] 
[Full GC (Ergonomics) [PSYoungGen: 2047K->2047K(2560K)] [ParOldGen: 7050K->7050K(7168K)] 9098K->9098K(9728K), [Metaspace: 3225K->3225K(1056768K)], 0.0336287 secs] [Times: user=0.11 sys=0.00, real=0.03 secs] 
[Full GC (Ergonomics) [PSYoungGen: 2047K->2047K(2560K)] [ParOldGen: 7051K->7051K(7168K)] 9099K->9099K(9728K), [Metaspace: 3225K->3225K(1056768K)], 0.0324847 secs] [Times: user=0.22 sys=0.00, real=0.03 secs] 
[Full GC (Ergonomics) [PSYoungGen: 2047K->2047K(2560K)] [ParOldGen: 7053K->7053K(7168K)] 9101K->9101K(9728K), [Metaspace: 3225K->3225K(1056768K)], 0.0342784 secs] [Times: user=0.22 sys=0.00, real=0.04 secs] 
[Full GC (Ergonomics) [PSYoungGen: 2047K->2047K(2560K)] [ParOldGen: 7055K->7055K(7168K)] 9103K->9103K(9728K), [Metaspace: 3225K->3225K(1056768K)], 0.0334541 secs] [Times: user=0.33 sys=0.00, real=0.03 secs] 
[Full GC (Ergonomics) [PSYoungGen: 2047K->2047K(2560K)] [ParOldGen: 7057K->7057K(7168K)] 9105K->9105K(9728K), [Metaspace: 3225K->3225K(1056768K)], 0.0341891 secs] [Times: user=0.22 sys=0.00, real=0.03 secs] 
[Full GC (Ergonomics) [PSYoungGen: 2047K->2047K(2560K)] [ParOldGen: 7058K->7058K(7168K)] 9106K->9106K(9728K), [Metaspace: 3225K->3225K(1056768K)], 0.0302677 secs] [Times: user=0.19 sys=0.00, real=0.03 secs] 
[Full GC (Ergonomics) [PSYoungGen: 2047K->2047K(2560K)] [ParOldGen: 7060K->7060K(7168K)] 9108K->9108K(9728K), [Metaspace: 3225K->3225K(1056768K)], 0.0326491 secs] [Times: user=0.19 sys=0.00, real=0.03 secs] 
[Full GC (Ergonomics) [PSYoungGen: 2047K->2047K(2560K)] [ParOldGen: 7062K->7062K(7168K)] 9110K->9110K(9728K), [Metaspace: 3225K->3225K(1056768K)], 0.0327378 secs] [Times: user=0.11 sys=0.00, real=0.03 secs] 
[Full GC (Ergonomics) [PSYoungGen: 2047K->2047K(2560K)] [ParOldGen: 7064K->7064K(7168K)] 9112K->9112K(9728K), [Metaspace: 3225K->3225K(1056768K)], 0.0325700 secs] [Times: user=0.09 sys=0.00, real=0.03 secs] 
[Full GC (Ergonomics) [PSYoungGen: 2047K->2047K(2560K)] [ParOldGen: 7065K->7065K(7168K)] 9113K->9113K(9728K), [Metaspace: 3225K->3225K(1056768K)], 0.0322844 secs] [Times: user=0.11 sys=0.00, real=0.03 secs] 
[Full GC (Ergonomics) [PSYoungGen: 2047K->2047K(2560K)] [ParOldGen: 7067K->7067K(7168K)] 9115K->9115K(9728K), [Metaspace: 3225K->3225K(1056768K)], 0.0269232 secs] [Times: user=0.11 sys=0.00, real=0.03 secs] 
[Full GC (Ergonomics) [PSYoungGen: 2047K->2047K(2560K)] [ParOldGen: 7069K->7069K(7168K)] 9117K->9117K(9728K), [Metaspace: 3225K->3225K(1056768K)], 0.0343916 secs] [Times: user=0.11 sys=0.00, real=0.03 secs] 
[Full GC (Ergonomics) [PSYoungGen: 2047K->2047K(2560K)] [ParOldGen: 7071K->7071K(7168K)] 9119K->9119K(9728K), [Metaspace: 3225K->3225K(1056768K)], 0.0329739 secs] [Times: user=0.09 sys=0.00, real=0.03 secs] 
[Full GC (Ergonomics) [PSYoungGen: 2047K->2047K(2560K)] [ParOldGen: 7072K->7072K(7168K)] 9120K->9120K(9728K), [Metaspace: 3225K->3225K(1056768K)], 0.0311813 secs] [Times: user=0.05 sys=0.00, real=0.03 secs] 
[Full GC (Ergonomics) [PSYoungGen: 2047K->2047K(2560K)] [ParOldGen: 7074K->7074K(7168K)] 9122K->9122K(9728K), [Metaspace: 3225K->3225K(1056768K)], 0.0385086 secs] [Times: user=0.05 sys=0.00, real=0.04 secs] 
[Full GC (Ergonomics) [PSYoungGen: 2047K->2047K(2560K)] [ParOldGen: 7078K->7058K(7168K)] 9126K->9106K(9728K), [Metaspace: 3228K->3228K(1056768K)], 0.0489022 secs] [Times: user=0.17 sys=0.00, real=0.05 secs] 
[Full GC (Ergonomics) [PSYoungGen: 2047K->2047K(2560K)] [ParOldGen: 7060K->7060K(7168K)] 9108K->9108K(9728K), [Metaspace: 3229K->3229K(1056768K)], 0.0297955 secs] [Times: user=0.14 sys=0.00, real=0.03 secs] 
[Full GC (Ergonomics) [PSYoungGen: 2047K->2047K(2560K)] [ParOldGen: 7061K->7061K(7168K)] 9109K->9109K(9728K), [Metaspace: 3229K->3229K(1056768K)], 0.0277327 secs] [Times: user=0.06 sys=0.00, real=0.03 secs] 
[Full GC (Ergonomics) [PSYoungGen: 2047K->2047K(2560K)] [ParOldGen: 7063K->7063K(7168K)] 9111K->9111K(9728K), [Metaspace: 3229K->3229K(1056768K)], 0.0337749 secs] [Times: user=0.25 sys=0.00, real=0.04 secs] 
[Full GC (Ergonomics) [PSYoungGen: 2047K->2047K(2560K)] [ParOldGen: 7065K->7065K(7168K)] 9113K->9113K(9728K), [Metaspace: 3229K->3229K(1056768K)], 0.0274153 secs] [Times: user=0.06 sys=0.00, real=0.03 secs] 
[Full GC (Ergonomics) [PSYoungGen: 2047K->2047K(2560K)] [ParOldGen: 7067K->7067K(7168K)] 9115K->9115K(9728K), [Metaspace: 3229K->3229K(1056768K)], 0.0325819 secs] [Times: user=0.16 sys=0.00, real=0.03 secs] 
[Full GC (Ergonomics) [PSYoungGen: 2047K->2047K(2560K)] [ParOldGen: 7069K->7069K(7168K)] 9117K->9117K(9728K), [Metaspace: 3229K->3229K(1056768K)], 0.0353046 secs] [Times: user=0.19 sys=0.00, real=0.04 secs] 
[Full GC (Ergonomics) [PSYoungGen: 2047K->2047K(2560K)] [ParOldGen: 7070K->7070K(7168K)] 9118K->9118K(9728K), [Metaspace: 3229K->3229K(1056768K)], 0.0310516 secs] [Times: user=0.22 sys=0.00, real=0.03 secs] 
[Full GC (Ergonomics) [PSYoungGen: 2047K->2047K(2560K)] [ParOldGen: 7072K->7072K(7168K)] 9120K->9120K(9728K), [Metaspace: 3229K->3229K(1056768K)], 0.0344718 secs] [Times: user=0.13 sys=0.00, real=0.04 secs] 
[Full GC (Ergonomics) [PSYoungGen: 2047K->2047K(2560K)] [ParOldGen: 7074K->7074K(7168K)] 9122K->9122K(9728K), [Metaspace: 3229K->3229K(1056768K)], 0.0291248 secs] [Times: user=0.09 sys=0.00, real=0.03 secs] 
[Full GC (Ergonomics) [PSYoungGen: 2047K->2047K(2560K)] [ParOldGen: 7076K->7076K(7168K)] 9124K->9124K(9728K), [Metaspace: 3229K->3229K(1056768K)], 0.0330405 secs] [Times: user=0.11 sys=0.00, real=0.03 secs] 
[Full GC (Ergonomics) [PSYoungGen: 2047K->2047K(2560K)] [ParOldGen: 7078K->7078K(7168K)] 9126K->9126K(9728K), [Metaspace: 3229K->3229K(1056768K)], 0.0394979 secs] [Times: user=0.16 sys=0.00, real=0.04 secs] 
[Full GC (Ergonomics) [PSYoungGen: 2047K->2047K(2560K)] [ParOldGen: 7079K->7079K(7168K)] 9127K->9127K(9728K), [Metaspace: 3229K->3229K(1056768K)], 0.0327822 secs] [Times: user=0.16 sys=0.00, real=0.03 secs] 
[Full GC (Ergonomics) [PSYoungGen: 2047K->2047K(2560K)] [ParOldGen: 7083K->7081K(7168K)] 9131K->9129K(9728K), [Metaspace: 3233K->3233K(1056768K)], 0.0320142 secs] [Times: user=0.17 sys=0.00, real=0.03 secs] 
[Full GC (Ergonomics) [PSYoungGen: 2047K->2047K(2560K)] [ParOldGen: 7083K->7083K(7168K)] 9131K->9131K(9728K), [Metaspace: 3234K->3234K(1056768K)], 0.0321638 secs] [Times: user=0.19 sys=0.00, real=0.03 secs] 
[Full GC (Ergonomics) [PSYoungGen: 2047K->2047K(2560K)] [ParOldGen: 7088K->7088K(7168K)] 9136K->9136K(9728K), [Metaspace: 3234K->3234K(1056768K)], 0.0324113 secs] [Times: user=0.11 sys=0.00, real=0.03 secs] 
[Full GC (Ergonomics) [PSYoungGen: 2047K->2047K(2560K)] [ParOldGen: 7090K->7090K(7168K)] 9138K->9138K(9728K), [Metaspace: 3234K->3234K(1056768K)], 0.0362137 secs] [Times: user=0.11 sys=0.00, real=0.04 secs] 
Exception in thread "main" [Full GC (Ergonomics) [PSYoungGen: 2047K->0K(2560K)] [ParOldGen: 7112K->633K(7168K)] 9160K->633K(9728K), [Metaspace: 3269K->3269K(1056768K)], 0.0058288 secs] [Times: user=0.11 sys=0.00, real=0.01 secs] 
java.lang.OutOfMemoryError: GC overhead limit exceeded
    at java.lang.Integer.toString(Integer.java:403)
    at java.lang.String.valueOf(String.java:3099)
    at GCOverheadDemo.main(GCOverheadDemo.java:13)
Heap
 PSYoungGen      total 2560K, used 126K [0x00000000ffd00000, 0x0000000100000000, 0x0000000100000000)
  eden space 2048K, 6% used [0x00000000ffd00000,0x00000000ffd1fa60,0x00000000fff00000)
  from space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)
  to   space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000)
 ParOldGen       total 7168K, used 633K [0x00000000ff600000, 0x00000000ffd00000, 0x00000000ffd00000)
  object space 7168K, 8% used [0x00000000ff600000,0x00000000ff69e6f8,0x00000000ffd00000)
 Metaspace       used 3334K, capacity 4500K, committed 4864K, reserved 1056768K
  class space    used 360K, capacity 388K, committed 512K, reserved 1048576K

Direct buffer memory

元空间的本质和永久代类似,都是对JVM规范中方法区的实现。不过元空间与永久代间最大的区别在于:元空间并不在虚拟机中,而是使用本地内存。因此,默认情况下,元空间的大小仅受本地内存限制。

写NIO程序经常使用ByteBuffer来读取或者写入数据,这是一种基于通道(Channel)与缓冲区(Buffer)的I/O方式,它可以使用Native函数库直接分配堆外内存,然后统一一个存储在Java堆里面的DirectByteBuffer对象作为这块内存的引用进行操作。这样能在一下场景中显著提高性能,因为避免了在Java堆和Native堆中来回复制数据。

ByteBuffer.allocate(capability)第一种方式分配JVM堆内存,属于GC管辖范围,由于需要拷贝索引速度相对较慢。

ByteBuffer.allocateDirect(capability)第二种方式是分配OS本地内存,不属于GC管辖范围,由于不需要内存拷贝索引速度相对较快。

但如果不断分配本地内存,堆内存很少使用,那么JVM久不需要执行GC,DirectByteBuffer对象们就不会被回收,这时候堆内存充足,但本地内存可能已经使用光了,再次尝试分配本地内存就会出现OutOfMemory,那程序就直接崩溃了。

import java.nio.ByteBuffer;

public class DriectBufferMemoryDemo {
    public static void main(String[] args) {
        System.out.println("配置的maxDirectMemory:"+sun.misc.VM.maxDirectMemory()/(double)1024/1024+"MB");
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        ByteBuffer buffer = ByteBuffer.allocateDirect(6 * 1024 * 1024);
    }
}

运行参数配置

-Xms10m -Xmx10m -XX:+PrintGCDetails -XX:MaxDirectMemorySize=5m

结果

配置的maxDirectMemory:5.0MB
[GC (Allocation Failure) [PSYoungGen: 2048K->488K(2560K)] 2048K->892K(9728K), 0.0008431 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[GC (System.gc()) [PSYoungGen: 1269K->504K(2560K)] 1673K->1132K(9728K), 0.0010115 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 
[Full GC (System.gc()) [PSYoungGen: 504K->0K(2560K)] [ParOldGen: 628K->1028K(7168K)] 1132K->1028K(9728K), [Metaspace: 3741K->3741K(1056768K)], 0.0091449 secs] [Times: user=0.13 sys=0.00, real=0.01 secs] 
Exception in thread "main" java.lang.OutOfMemoryError: Direct buffer memory
    at java.nio.Bits.reserveMemory(Bits.java:694)
    at java.nio.DirectByteBuffer.<init>(DirectByteBuffer.java:123)
    at java.nio.ByteBuffer.allocateDirect(ByteBuffer.java:311)
    at DriectBufferMemoryDemo.main(DriectBufferMemoryDemo.java:11)
Heap
 PSYoungGen      total 2560K, used 49K [0x00000000ffd00000, 0x0000000100000000, 0x0000000100000000)
  eden space 2048K, 2% used [0x00000000ffd00000,0x00000000ffd0c5d0,0x00000000fff00000)
  from space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)
  to   space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000)
 ParOldGen       total 7168K, used 1028K [0x00000000ff600000, 0x00000000ffd00000, 0x00000000ffd00000)
  object space 7168K, 14% used [0x00000000ff600000,0x00000000ff7011b0,0x00000000ffd00000)
 Metaspace       used 3772K, capacity 4536K, committed 4864K, reserved 1056768K
  class space    used 414K, capacity 428K, committed 512K, reserved 1048576K

unable to create new native thread

高并发请求服务器时,经常出现如下异常: java.lang.OutOfMemoryError: unable to create new native thread,准确地讲该native thread异常与对应的平台有关

导致原因:

你的应用创建了太多线程了,一个应用进程创建多个线程,超过系统承载极限

你的应用创建超过这个数量,就会报java.lang.OutOfMemoryError: unable to create new native thread

解决办法:

想办法降低你应用程序创建线程的数量,分析应用是否真的需要创建这么多线程,如果不是,改代码将线程数降到最低

对于有的应用,确实需要创建很多线程,远超过Linux系统的默认1024个线程的限制,可以通过修改Linux服务器配置,扩大Linux默认限制

public class UnableCreateNewThreadDemo {
    public static void main(String[] args) {
        for(int i = 1;;i++){
            System.out.println("********** i = "+i);
            new Thread(()->{
                try {
                    Thread.sleep(Integer.MAX_VALUE);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

编译运行

[es@192 tmp]$ javac -d . UnableCreateNewThreadDemo.java 
[es@192 tmp]$ java UnableCreateNewThreadDemo
********** i = 4075
********** i = 4076
Exception in thread "main" java.lang.OutOfMemoryError: unable to create new native thread
        at java.lang.Thread.start0(Native Method)
        at java.lang.Thread.start(Thread.java:717)
        at UnableCreateNewThreadDemo.main(UnableCreateNewThreadDemo.java:11)

^CJava HotSpot(TM) 64-Bit Server VM warning: Exception java.lang.OutOfMemoryError occurred dispatching signal SIGINT to handler- the VM may need to be forcibly terminated
^CJava HotSpot(TM) 64-Bit Server VM warning: Exception java.lang.OutOfMemoryError occurred dispatching signal SIGINT to handler- the VM may need to be forcibly terminated
Java HotSpot(TM) 64-Bit Server VM warning: Exception java.lang.OutOfMemoryError occurred dispatching signal SIGTERM to handler- the VM may need to be forcibly terminated
^CJava HotSpot(TM) 64-Bit Server VM warning: Exception java.lang.OutOfMemoryError occurred dispatching signal SIGINT to handler- the VM may need to be forcibly terminated
Killed  

以root用户登录开启另外一个终端

[root@192 ~]# jps
8257 Jps
4120 UnableCreateNewThreadDemo
[root@192 ~]# kill -9 4120

修改用户线程数

[root@192 ~]# ulimit -u
4096
[root@192 ~]# vim /etc/security/limits.d/90-nproc.conf 

1564647962235

Metaspace

Java8及之后的版本使用Metaspace来替代永久代。

Metaspace是方法区在HotSpot的实现,它与永久代最大的区别在于:Metaspace并不在虚拟机内存中而是使用本地内存,也即在java8中,classe metadata(the virtual machines internal presentation of java class),被存储在叫做Metaspace的native memory

永久代(java8后被元空间Metaspace取代了)存放了一下信息:

  • 虚拟机加载类信息
  • 常量池
  • 静态变量
  • 即时编译后的代码

模拟Metaspace空间溢出,我们不断生成类往元空间灌,类占据的空间总是会超过Metaspace指定的空间大小的

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class MetaspaceOOMTest {

    public static void main(String[] args) {
        int i = 0;

        try {
            while (true) {
                i++;
                Enhancer enhancer = new Enhancer();
                enhancer.setSuperclass(OOMTest.class);
                enhancer.setUseCache(false);
                enhancer.setCallback(new MethodInterceptor() {
                    @Override
                    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                        return methodProxy.invokeSuper(o, args);
                    }
                });
                enhancer.create();
            }

        } catch (Exception e) {
            System.out.println("*************多少次后发生了异常:" + i);
            e.printStackTrace();
        }

    }

    static class OOMTest {
    }
}

运行配置参数

-XX:MetaspaceSize=8m -XX:MaxMetaspaceSize=8m

   转载规则


《JUC多线程并发、JVM和GC》 shenlibing 采用 知识共享署名 4.0 国际许可协议 进行许可。
  目录