北京--面试1(设计模式、反射、队列、线程、锁、Linux命令、JVM调优参数)

1、写三个设计模式(代码)

//单例懒汉模式:单例模式确保一个类只有一个实例,并提供一个全局访问点。在Java中,单例模式被广泛用于控制资源访问,配置管理器等场景。实现单例模式的方式包括懒汉式、饿汉式、双重检查锁等。
public class Singleton {
    private static volatile Singleton instance;
    private Singleton() {}
    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

//工厂模式:工厂模式用于创建对象,但隐藏了对象的创建逻辑。通过定义一个接口或抽象类,工厂模式允许子类决定实例化哪个类。在Java中,工厂模式常被用于处理对象的创建,使得代码更具灵活性和可维护性。
  interface Car {
    void drive();
}

class MPV implements Car {
    @Override
    public void drive() {}
}

class SUV implements Car {
    @Override
    public void drive() {}
}

class CarFactory {
    public Car getCar(String carType) {
        if (carType == null) {
            return null;
        }
        if (carType.equalsIgnoreCase("MPV")) {
            return new MPV();
        } else if (carType.equalsIgnoreCase("SUV")) {
            return new SUV();
        }
        return null;
    }
}
//代理模式-动态代理:动态代理模式是一种在运行时动态创建代理对象的机制,它允许开发者在无需修改原有代码的基础上,为特定接口或类的对象提供额外的逻辑或功能,如方法拦截、增强、日志记录、权限检查、事务管理等。
//这种模式实现了代理设计模式的动态版本,其中代理对象与委托对象(即被代理的对象)具有相同的接口,代理对象在方法调用前后可以执行额外的操作,然后将调用转发给委托对象。
// 定义一个接口,被代理对象需要实现该接口
public interface MyInterface {
    void doSomething();
}

// 被代理对象,实现MyInterface接口
public class RealObject implements MyInterface {
    @Override
    public void doSomething() {
        System.out.println("Real object doing something...");
    }
}

// 实现InvocationHandler接口,提供代理逻辑
public class MyInvocationHandler implements InvocationHandler {
    private Object target; // 被代理对象

    public MyInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Before method call: " + method.getName());
        
        // 调用目标对象的方法
        Object result = method.invoke(target, args);
        
        System.out.println("After method call: " + method.getName());

        return result;
    }
}

public class DynamicProxyExample {
    public static void main(String[] args) {
        // 创建被代理对象
        MyInterface realObject = new RealObject();

        // 创建InvocationHandler实例,传入被代理对象
        InvocationHandler handler = new MyInvocationHandler(realObject);

        // 通过Proxy.newProxyInstance创建动态代理对象
        MyInterface proxy = (MyInterface) Proxy.newProxyInstance(
                MyInterface.class.getClassLoader(),
                new Class[]{MyInterface.class},
                handler
        );

        // 通过代理对象调用方法,此时会触发代理逻辑
        proxy.doSomething();
    }
}

2、不使用Java自带的BlockingQueue,实现一个阻塞队列,要求有删除和增加方法以及线程安全--考察阻塞队列(代码)

(1)先看下 JDK17 中 ArryBlockingQueue 的实现方式

主要思想依旧是基于数组实现阻塞队列。ArrayBlockingQueuejava.util.concurrent 包下的一个类,实现了 BlockingQueue 接口,提供了一个有界的阻塞队列,其中元素按 FIFO(先进先出)的顺序进行排序。

数据结构: ArrayBlockingQueue 内部使用一个定长数组(Object[] items)存储元素,并维护两个索引变量:

   takeIndex:指向下一个可供消费者取出(take() 或 poll())的元素位置。

   putIndex:指向下一个可供生产者放入(put() 或 offer())新元素的位置。

线程同步: 为了保证线程安全性,ArrayBlockingQueue 使用一个可重入锁(ReentrantLock)来保护队列的访问,并且使用两个相关的条件变量(notEmpty 和 notFull)来实现阻塞和唤醒操作。当队列满时,尝试入队的线程会被阻塞在 notFull 上;当队列空时,尝试出队的线程会被阻塞在 notEmpty 上。

主要方法实现

  1. 入队操作

  2. put(E e):将元素添加到队列尾部。如果队列已满,则当前线程会被阻塞,直到有空间可用(即有其他线程消费了一个元素,使得队列不再满)。    

  3. offer(E e):类似 put(),但非阻塞版本。如果队列满,立即返回 false,不等待。

  4. 出队操作

  5. take():从队列头部移除并返回一个元素。如果队列为空,则当前线程会被阻塞,直到有元素可用(即有其他线程生产了一个元素,使得队列不再空)。poll():类似 take(),但非阻塞版本。如果队列空,立即返回 null,不等待。

  6. 容量控制

  7. ArrayBlockingQueue 在构造时指定固定容量,一旦创建,容量不可变。

  8. 入队时检查 putIndex 是否超过数组长度,若超过则回绕至数组起始处。若此时 takeIndex == putIndex(即队列已满),则阻塞生产者线程。

  9. 出队时检查 takeIndex 是否超过数组长度,若超过则回绕至数组起始处。若此时 takeIndex == putIndex(即队列已空),则阻塞消费者线程。

  10. 其他方法

  11. peek():查看队头元素但不移除,若队列为空返回 null

  12. size()remainingCapacity()isEmpty()isFull() 等方法用于查询队列状态,均受内部锁保护,确保线程安全。

性能优化: ArrayBlockingQueue 的实现考虑了性能优化:

       使用循环数组实现,避免数组复制或扩容操作。

       利用条件变量精确地唤醒等待的线程,避免不必要的“惊群效应”(即一次性唤醒所有等待线程,只有一个是真正需要工作的)

public class ArrayBlockingQueue<E> extends AbstractQueue<E> implements BlockingQueue<E>, java.io.Serializable {
    
    private static final long serialVersionUID = -817911632652898426L;

    // Conditionally serializable
    private final Object[] items;

    //items index for next take, poll, peek or remove
    private int takeIndex;

    //items index for next put, offer, or add
    private int putIndex;

    //Number of elements in the queue
    private int count;
    
    //并发控制使用经典的双条件算法
    //Main lock guarding all access
    private final ReentrantLock lock;

    //Classes implementing Condition may be serializable
    private final Condition notEmpty;

    //Classes implementing Condition may be serializable
    private final Condition notFull;
    
    /**
     * 行参:capacity - 此队列的容量
     *      fair - 如果是 true 插入或删除时阻塞的队列的线程访问按照FIFO顺序处理;如果 false未指定访问顺序
     *     如果 capacity < 1 则抛出异常 IllegalArgumentException
     */ 
    public ArrayBlockingQueue(int capacity, boolean fair) {
        if (capacity <= 0)
            throw new IllegalArgumentException();
        this.items = new Object[capacity];
        lock = new ReentrantLock(fair);
        notEmpty = lock.newCondition();
        notFull =  lock.newCondition();
    }

    // 入队操作
    public void put(E e) throws InterruptedException {
        // 检查入队的元素是否为空,如果为空则抛出 NullPointerException
        Objects.requireNonNull(e);
        // 获取可重入锁对象
        final ReentrantLock lock = this.lock;
        // 尝试获取锁,如果被中断则抛出 InterruptedException
        lock.lockInterruptibly();
        try {
            // 当队列已满时,线程阻塞等待直到队列有空间可用
            while (count == items.length)
                notFull.await();
            // 将元素加入队列
            enqueue(e);
        } finally {
            // 无论如何都要释放锁,确保锁的正常释放
            lock.unlock();
        }
    }

    // 出队操作
    public E take() throws InterruptedException {
        // 获取可重入锁对象
        final ReentrantLock lock = this.lock;
        // 尝试获取锁,如果被中断则抛出 InterruptedException
        lock.lockInterruptibly();
        try {
            // 当队列为空时,线程阻塞等待直到队列中有元素可取
            while (count == 0)
                notEmpty.await();
            // 从队列中取出元素并返回
            return dequeue();
        } finally {
            // 无论如何都要释放锁,确保锁的正常释放
            lock.unlock();
        }
    }

}

(2)自己实现 ,仿照官方的来写就可以了

import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class BlockingQueue<E> {
    private final Queue<E> queue;
    private final int capacity;
    private final Lock lock;
    private final Condition notFull;
    private final Condition notEmpty;

    public BlockingQueue(int capacity) {
        this.capacity = capacity;
        this.queue = new ArrayList<>();
        this.lock = new ReentrantLock();
        this.notFull = lock.newCondition();
        this.notEmpty = lock.newCondition();
    }

    public void enqueue(E element) throws InterruptedException {
        lock.lock();
        try {
            while (queue.size() == capacity) {
                // 队列已满,等待队列不满的条件
                notFull.await();
            }
            queue.offer(element);
            // 入队后唤醒可能在等待队列不空的线程
            notEmpty.signal();
        } finally {
            lock.unlock();
        }
    }

    public E dequeue() throws InterruptedException {
        lock.lock();
        try {
            while (queue.isEmpty()) {
                // 队列为空,等待队列不空的条件
                notEmpty.await();
            }
            E element = queue.poll();
            // 出队后唤醒可能在等待队列不满的线程
            notFull.signal();
            return element;
        } finally {
            lock.unlock();
        }
    }
}

3、怎么获取一个类的注解--考察反射(代码)

1、Class<?> clazz = Class.forName(className)

作用:通过全限定类名字符串动态加载并获取对应的Class对象。这是获取类的元数据入口点,所有反射操作都围绕Class对象展开。

2、Constructor<?>[] constructors = clazz.getConstructors()

作用:获取类的所有公共构造器(包括其参数类型、修饰符等信息)。可以进一步调用构造器的newInstance()方法创建对象。

3、Constructor<?> constructor = clazz.getDeclaredConstructor(paramTypes...)

作用:获取类的指定签名的构造器(包括私有构造器)。同样可用于创建对象。

4、Method[] methods = clazz.getMethods()

作用:获取类及其父类中所有公开的(包括继承的)方法。可用于获取方法名、返回类型、参数列表等信息,并通过invoke()方法调用它们。

5、Method method = clazz.getDeclaredMethod(methodName, paramTypes...)

作用:获取类中指定签名的(包括私有、受保护的)方法。同样可用于获取方法详情和调用方法。

6、Field[] fields = clazz.getFields()

作用:获取类及其父类中所有公开的(包括继承的)字段(属性)。可以获取字段名、类型、修饰符,并通过get()set()方法访问或修改字段值。

7、Field field = clazz.getDeclaredField(fieldName)

作用:获取类中指定名称的(包括私有、受保护的)字段。同样可用于获取字段详情和访问或修改字段值。

8、Object obj = clazz.newInstance()

作用:如果类有一个无参的公共构造器,此方法用于创建该类的新实例。等价于调用clazz.getConstructor().newInstance()

9、T result = (T) method.invoke(obj, args...)

作用:在给定对象上调用指定方法,并传递参数。返回方法的执行结果(如果有的话)。允许调用私有方法和访问受保护的属性。

10、field.setAccessible(true)

作用:设置字段或方法为可访问,即使它们原本是非公有的(如私有或受保护)。这对于反射操作中访问或修改私有成员非常关键。

11、AnnotatedType annotatedType = field.getAnnotatedType()

作用:获取字段的注解类型信息,包括注解及其元注解。用于处理元数据级别的自定义注解。

12、Class<?> superclass = clazz.getSuperclass()

作用:获取类的直接超类。可用于递归遍历类的继承层次结构。

13、Type[] genericInterfaces = clazz.getGenericInterfaces()

作用:获取类实现的所有泛型接口及它们的类型参数。对于理解类的泛型类型信息很有帮助。

14、Annotation[] annotations = clzz.getAnnotations();

作用:获取此类下所有的注解

4、同步锁(synchronized 、 ReentrantLock )的替代方案--考察对同步锁的理解(简述)

为什么要替代同步锁呢?主要有以下几个原因:

1、性能问题: 同步锁可能会引起线程之间的竞争,从而导致性能下降。另外,同步锁的粒度较大时,可能会导致线程间的阻塞,进一步影响性能。

2、死锁问题: 同步锁可能会引起死锁,当多个线程相互等待对方释放锁时,会出现死锁情况,导致程序无法继续执行。

3、复杂性: 使用同步锁需要开发人员手动管理锁的获取和释放,容易出现错误,增加了代码的复杂性和维护成本。

4、可扩展性: 使用同步锁会导致代码的耦合性增加,降低了代码的可扩展性和可重用性。使用替代方案可以更灵活地管理并发访问,提高了代码的可扩展性。 

同步锁的一些常见替代方案:

1、并发集合类(Concurrent Collections): Java并发包中提供了一些并发安全的集合类,如 ConcurrentHashMapConcurrentLinkedQueue 等。这些集合类是线程安全的,并且通常比使用同步锁更高效。

2、原子类(Atomic Classes): Java提供了一系列的原子类,如 AtomicIntegerAtomicReference 等,它们提供了一种无锁的线程安全的方式来更新共享变量的值。

3、并发工具类(Concurrency Utilities): Java并发包中提供了各种并发工具类,如 CountDownLatchSemaphoreCyclicBarrier 等,它们可以帮助我们更灵活地管理并发执行的任务。

4、无锁数据结构(Lock-Free Data Structures): 一些无锁数据结构可以实现高效的并发访问,如无锁队列、无锁链表等。这些数据结构通常使用原子操作来实现,避免了同步锁的性能开销和可能的死锁问题。

5、软件事务内存(Software Transactional Memory,STM): STM是一种高级的并发控制机制,它将代码块包装在事务中,类似于数据库中的事务,可以在事务中执行一系列的读写操作,然后提交或回滚。但在Java中,STM并不是标准库的一部分,需要使用第三方库实现。

5、快速排序(代码/思路)

快速排序是一种分治策略的排序算法,通过选取一个“基准”元素将数组划分为两个子序列,左边的元素小于基准,右边的元素大于基准。然后递归地对左右子序列进行同样的操作。  

public class QuickSort {
    // 快速排序方法,采用分治法策略
    public static void quickSort(int[] arr, int low, int high) {
        if (low < high) {
            // 找到分区点(pivot),将数组分为两部分:小于pivot的元素和大于pivot的元素
            int pivotIndex = partition(arr, low, high);
 
            // 对左右两边的子数组进行递归排序
            quickSort(arr, low, pivotIndex - 1);
            quickSort(arr, pivotIndex + 1, high);
        }
    }
 
    // 分区操作,返回pivot的位置
    private static int partition(int[] arr, int low, int high) {
        // 选取最后一个元素作为基准值
        int pivot = arr[high];
        int i = (low - 1);  // index of smaller element
 
        for (int j = low; j <= high - 1; j++) {
            // 如果当前元素小于或等于pivot,则交换位置并将较小元素索引后移
            if (arr[j] <= pivot) {
                i++;
 
                // 交换arr[i]和arr[j]
                int temp = arr[i];
                arr[i] = arr[j];
                arr[j] = temp;
            }
        }
 
        // 将基准值与i+1位置上的元素交换,保证基准值左边的都比它小,右边的都比它大
        int temp = arr[i + 1];
        arr[i + 1] = arr[high];
        arr[high] = temp;
 
        return i + 1;
    }
 
    public static void main(String[] args) {
        int[] array = {9, 7, 5, 11, 12, 2, 14, 3, 10, 6};
        quickSort(array, 0, array.length - 1);
 
        System.out.println("Sorted array: ");
        for (int num : array) {
            System.out.print(num + " ");
        }
    }
}

6、有 t1、t2、t3 三个线程,保证 t1、t2 同时执行, t3 在t1、t2之后执行(代码)

(1)使用计数器

通过计数法(倒计时器),让一些线程堵塞直到另一个线程完成一系列操作后才被唤醒;该⼯具通常⽤来控制线程等待,它可以让某⼀个线程等待直到倒计时结束,再开始执⾏。具体可以使用countDownLatch.await()来等待结果。多用于多线程通信。

  @Test
  void CountDownLatchTest() {
        //通过计数法(倒计时器)
        CountDownLatch latch = new CountDownLatch(2);

        Thread t1 = new Thread(() -> {
            System.out.println("Thread t1 is running");
            //省略模拟耗时操作
            latch.countDown();
        });

        Thread t2 = new Thread(() -> {
            System.out.println("Thread t2 is running");
            //省略模拟耗时操作
            latch.countDown();
        });

        Thread t3 = new Thread(() -> {
            try {
                latch.await(); // 等待t1和t2执行完毕
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            System.out.println("Thread t3 is running after t1 and t2");
        });

        t1.start();
        t2.start();
        t3.start();
    }

(2)使用CompletableFuture 

通过设置参数,可以完成CountDownLatch同样的多平台响应问题,但是可以针对其中部分返回结果做更加灵活的展示。

    @Test
    void CompletableFutureTest() {
        // 创建 CompletableFuture 对象
        CompletableFuture<Void> future1 = CompletableFuture.runAsync(() -> {
            // 模拟耗时操作
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("Task t1 finished");
        });

        CompletableFuture<Void> future2 = CompletableFuture.runAsync(() -> {
            // 模拟耗时操作
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("Task t2 finished");
        });

        // 使用 thenCombine 方法等待 future1 和 future2 完成后执行 t3 线程
        CompletableFuture<Void> combinedFuture = future1.thenCombine(future2, (result1, result2) -> {
            System.out.println("Task t3 can run after t1 and t2 finished");
            return null;
        });

        try {
            // 等待 t3 线程执行完成
            combinedFuture.get();
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }

(3) 使用CyclicBarrier

可循环(Cyclic)使用的屏障(Barrier)。他要做的事情是,让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续干活,线程进入屏障通过CyclicBarrier的await()方法。可以用于批量发送消息队列信息、异步限流。 

    @Test
    void CyclicBarrierTest() {
        CyclicBarrier barrier = new CyclicBarrier(3);
        Thread t1 = new Thread(new CyclicBarrierTask("Thread 1", barrier));
        Thread t2 = new Thread(new CyclicBarrierTask("Thread 2", barrier));
        Thread t3 = new Thread(new CyclicBarrierTask("Thread 3", barrier));
        t1.start();
        t2.start();
        t3.start();
        try {
            t1.join();
            t2.join();
            t3.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    class CyclicBarrierTask implements Runnable {

        final String name;
        final CyclicBarrier barrier;

        CyclicBarrierTask(String name, CyclicBarrier barrier) {
            this.name = name;
            this.barrier = barrier;
        }


        @Override
        public void run() {
            System.out.println(name + " started.");
            try {
                // 执行任务...
                Thread.sleep((int) (Math.random() * 1000)); // 模拟耗时任务
                // 到达屏障,等待其他线程
                barrier.await();
                System.out.println(name + " finished.");
            } catch (InterruptedException | BrokenBarrierException e) {
                e.printStackTrace();
            }
        }
    }

(4)使用Semaphore(信号量)

信号量主要用于两个目的,一个是用于多个共享资源的互斥作用,另一个用于并发线程数的控制。SpringHystrix限流的思想 

    @Test
    void SemaphoreTest(){
        Semaphore semaphore = new Semaphore(0); // 初始计数为0
        Thread t1 = new Thread(new SemaphoreTask(semaphore, "Thread 1"));
        Thread t2 = new Thread(new SemaphoreTask(semaphore, "Thread 2"));
        Thread t3 = new Thread(new SemaphoreTask(semaphore, "Thread 3"));

        t1.start();
        t2.start();

        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        t3.start();
    }
    static class SemaphoreTask implements Runnable {
        private Semaphore semaphore;
        private String name;

        public SemaphoreTask(Semaphore semaphore, String name) {
            this.semaphore = semaphore;
            this.name = name;
        }

        @Override
        public void run() {
            if (name.equals("Thread 3")) {
                try {
                    semaphore.acquire(); // 等待许可
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(name + " is executing after Thread 1 and Thread 2");
            } else {
                System.out.println(name + " is executing");
                semaphore.release(); // 释放许可
            }
        }
    }

7、Linux命令:1、查找文件并删除;2、后台启动服务;3、使用java命令合理分配新生代和老年代

(1) 查找一个文件并删除它

find:

是一个强大的文件查找工具,可以递归地搜索指定目录及其子目录,并根据各种条件(如文件名、大小、类型、修改时间等)进行匹配。
locate:

基于系统维护的一个文件索引数据库
whereis:

主要用于查找二进制文件、源代码文件和帮助手册页的路径,适用于查找系统命令或程序相关文件。对于非系统级别的用户文件,可能无法找到
which:

命令用于查找可执行文件的路径,它会在环境变量 PATH 指定的目录列表中查找。主要用于查找系统命令或用户自定义的可执行脚本的路径。

第一种:find /path/to/search -name "filename" -type f -delete   
第二种:已知一个文件路径,使用   rm 文件路径   也可以删除

/path/to/search: 要在哪个目录及其子目录下查找文件。请替换为实际的路径,如 /home/user 或 /var/log。
-name "filename": 根据文件名进行匹配。将 "filename" 替换为你想要查找的具体文件名,可以使用通配符(如 *.txt 或 file*)。
-type f: 只查找普通文件,避免删除目录。
-delete: 如果找到匹配的文件,直接删除它。

(2) 后台启动服务,实际就是deamon方式启动

nohup java -jar xxx.jar > app.log 2>&1 

标准输出(stdout)被重定向到app.log文件,标准错误输出(stderr)通过2>&1被合并到标准输出中,同样写入app.log

(3) 使用java命令分配年轻带和老年代(优化代码结构 --> JVM参数调优)

-XX:+ :‘+’ 表示启用该选项

-XX:- :‘-’ 表示关闭该选项

-classpath-cp:指定 Java 类路径,用于告诉 JVM 在哪里查找类文件。

-D:用于设置系统属性。例如,-Dproperty=value 可以设置名为 property 的系统属性的值为 value

-Xmx:设置 Java 堆的最大内存。例如,-Xmx512m 表示最大堆大小为 512MB。

-Xms:设置 Java 堆的初始大小。例如,-Xms256m 表示初始堆大小为 256MB。

-Xmn:设置年轻代大小,增大年轻代后,将会减小年老代大小,官方推荐配置为整个堆的3/8。例如,-Xmn128m 表示年轻代大小为 128MB。

-Xss:设置线程堆栈大小。根据应用的递归深度、本地变量数量等因素调整,避免栈溢出或浪费内存。例如,-Xss1m 表示每个线程的堆栈大小为 1MB。

-XX:PermSize:设置永久代(PermGen)的初始大小。在 Java 8 及更高版本中已废弃

-XX:MaxPermSize:设置永久代(PermGen)的最大大小。在 Java 8 及更高版本中,PermGen 被元空间(Metaspace)取代。

-XX:MetaspaceSize:设置 Metaspace 的初始大小。例如,-XX:MetaspaceSize=256m 表示 Metaspace 的初始大小为 256MB。

-XX:MaxMetaspaceSize:设置 Metaspace 的最大大小。例如,-XX:MaxMetaspaceSize=256m 表示 Metaspace 的最大大小为 256MB。

-XX:MaxDirectMemorySize:设置最大直接内存(堆外)大小,例如,-XX:MaxDirectMemorySize=256m 表示最大直接内存为 256MB。

-verbose:gc:启用 GC 日志输出,用于调试和性能分析。

-XX:+PrintGCDetails:打印 GC 详细信息,默认关闭,可以通过jinfo -flag [+|-]PrintGCDetails <pid> 或 jinfo -flag PrintGCDetails=<value> <pid> 来动态开启或设置值

-Xloggc:/data/gclog/gc.log:GC日志文件路径

-XX:+UseGCLogFileRotation:滚动GC日志文件,须配置Xloggc

-XX:NumberOfGCLogFiles:滚动GC日志文件数,默认0,不滚动

-XX:GCLogFileSize:GC文件滚动大小,需配置UseGCLogFileRotation,设置为0表示仅通过jcmd命令触发

-XX:+PrintGCDateStamps:GC时打印时间戳信息,默认关闭

-XX:+PrintTenuringDistribution:打印存活实例年龄信息,默认关闭

-XX:+PrintGCApplicationStoppedTime:打印应用暂停时间,默认关闭

-XX:+PrintHeapAtGC:GC前后打印堆区使用信息,默认关闭

-XX:+UseG1GC:指定使用 G1 垃圾回收器。

-XX:MaxGCPauseMillis:设置目标停顿时间

-XX:InitiatingHeapOccupancyPercent:设置触发并发标记周期的堆占用百分比。

-XX:+HeapDumpOnOutOfMemoryError:当发生 OutOfMemoryError 错误时自动生成堆转储文件。

-XX:HeapDumpPath:指定堆转储文件的输出路径。

-XX:-OmitStackTraceInFastThrow:某些热点异常抛的太多的话,JVM默认会做优化,会使用JVM初始化的时候创建的异常代替实际的异常,这些异常是没有异常栈信息的,不方便定位问题,如果有碰到这种情况,可以考虑关闭这个配置

-XX:NewRatio:设置新生代和老年代的大小比例,例如 -XX:NewRatio=2

-XX:SurvivorRatio:设置 Eden 区和 Survivor 区大小比例,例如,-XX:SurvivorRatio=8 表示两个Survivor区与一个Eden区比值为2:8,一个Survivor占1/10

-XX:MaxTenuringThreshold:设置对象晋升到老年代的年龄阈值,当超过这个参数值时就进入老年代,最大支持15

-XX:+UseSerialGC:年轻代使用Serial垃圾收集器,不推荐使用,性能太差,老年代将会使用SerialOld垃圾收集器

-XX:InitialCodeCacheSize-XX:ReservedCodeCacheSize:设置代码缓存的初始大小和最大大小。

-XX:+ParallelCompilation:启用并行编译,对于CPU密集型应用,启用并行编译可以加快类的加载速度

-XX:CompileThreshold:指定方法被调用多少次后进行即时编译。

-XX:PretenureSizeThreshold:设置该参数,可以使大于这个值的对象直接在老年代分配,避免在Eden区和Survivor区发生大量的内存复制,该参数只对Serial和ParNew收集器有效,Parallel Scavenge并不认识该参数

-XX:ParallelGCThreads=16:并行执行gc的线程数

-XX:MaxGCPauseMillis:自适应大小策略的最大GC暂停时间目标(以毫秒为单位),或(仅G1)每个MMU时间片的最大GC时间

-XX:+DisableExplicitGC:禁止运行期显式地调用System.gc()来触发fulll GC​​​​​​​,不建议开启,如果开启了这个参数可能会导致堆外内存无法及时回收造成内存溢出

  • 19
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值