尚硅谷周阳面试第二季(主要是juc和JVM)

2019年面试题

周老师讲课深入浅出,抽象化的概念会讲的比较通俗具象化,学习效果会比自己看书好很多,陆陆续续看了一个多月,终于看完了,估计以后肯定会忘,所以做了笔记以备日后复习。顺便不再做伸手党,自己分享出去一些知识。

1.请谈谈对volatile的理解

  • 1.1 volatile是java虚拟机提供的轻量级的同步机制
    1.1.1保证可见性
    1.1.2不保证原子性
    1.1.3禁止指令重排
  • 1.2JMM(Java内存模型Java Memory Model,简称JMM)本身是一种
    抽象的概念并不真实存在,它描述的是一组规则或规范,通过这
    组规范定义了程序中各个变量(包括实例字段、静态字段和构成
    数组对象的元素)的访问方式。
    • JMM关于同步的规定:
      • 1.线程解锁前,必须把共享变量的值刷新回主内存
      • 2.线程加锁前,必须读取主内存的最新值到自己的工作内存
      • 3.加锁解锁是同一把锁
        由于JVM运行程序的实体是线程,而每个线程创建是JVm都会为其创建一个工作内存(有些地方成为栈空间),工作内存是每个线程的私有数据区域,而java内存模型中规定所有变量都存储在主内存,主内存是共享内存区域,所有线程都可以访问,**但线程对变量的操作(读取赋值等)必须在工作内存中进行,首先要将变量从主内存拷贝的自己的工作内存空间,然后对变量进行操作,操作完成后再降变量写回主内存,**不能直接操作主内存中的变量,各个线程中的工作内存中存储着主内存中的变量副本拷贝,因此不同的线程间无法访问对方的工作内存,线程间的通信(传值)必须通过主内存来完成,其简单访问过程如下图:
        在这里插入图片描述
        可见性、原子性、有序性
    • 双重检查单例模式
class SingleTon{
    private static  SingleTon instance;
    private SingleTon (){
        System.out.println(Thread.currentThread().getName()+"----我是DCL,双重检查,听说极小概率是线程不安全的");
    }
    public static SingleTon getInstance(){
        if ( instance == null) {
            synchronized (SingleTon.class){
                if (instance == null) {
                    instance = new SingleTon();
                }
            }
        }
        return  instance;
    }
}

DCL(双端检测)机制不一定线程安全,原因是指令重排序的存在,加入volatile可以禁止指令重排
原因在于一个线程执行到第一次检测,读取到instance不为null时,instance的引用对象可能没有完成初始化
instance = new SingleTon();可以分成以下3步完成(伪代码)
memory = allocate();1.分配对象内存空间
instance(memory);2.初始化对象
instance = memory;3.设置instance指向刚分配的内存地址,此时instance!= null

步骤2和步骤3不存在依赖关系,而且无论重排前还是重排后程序的执行结果在单线程中并没有改变。
因此这种重排优化是允许的。
memory = allocate();1.分配对象内存空间
instance = memory;3.设置instance指向刚分配的内存地址,此时instance!= null ,但是对象还没有初始化完成!
instance(memory);2.初始化对象
但是指令重排只会保证串行语义的执行的一致性(单线程),但并不关心多线程间的语义一致性。所以当一条线程访问instance不为空时,由于instance实例未必已经初始化完成,也就造成了线程安全问题。

**线程安全版**
class SingleTon{
    private static  **volatile**  SingleTon instance;
    private SingleTon (){
        System.out.println(Thread.currentThread().getName()+"----我是DCL,双重检查,听说极小概率是线程不安全的");
    }
    public static SingleTon getInstance(){
        if ( instance == null) {
            synchronized (SingleTon.class){
                if (instance == null) {
                    instance = new SingleTon();
                }
            }
        }
        return  instance;
    }
}

2.CAS(CompareAndSet)比较并交换

  • atomicInteger.getAndIncredment()
    在这里插入图片描述
  • unSafe
    在这里插入图片描述
    在这里插入图片描述
  • CAS是什么
    在这里插入图片描述
    在这里插入图片描述
  • CAS缺点
    • 循环时间长开销很大 在这里插入图片描述

    • 只能保证一个共享变量的原子操作

    • 引出来ABA问题

3.原子类AtomicInteger的ABA问题谈谈?原子更新引用知道吗?

  • ABA问题
    在这里插入图片描述

4.ArrayList是线程不安全,请编码写一个不安全的案例,并给出解决方案

new ArrayList();创建一个object类型的空数组,有add操作后会创建一个容量为10的相应类型的数组。扩容为原来的1.5倍。

public static void main(String[] args) {
        ArrayList<Object> list = new ArrayList<>();
        for (int i = 0; i <50 ; i++) {
            new Thread( () -> {
                list.add(UUID.randomUUID().toString().substring(0,8));
                System.out.println(list);
            },String.valueOf(i)).start();
        }
//        java.util.ConcurrentModificationException 并发修改异常
    }

1.故障现象
java.util.ConcurrentModificationException 并发修改异常
2.导致原因
并发修改导致的线程不安全问题
3.解决方案
new Vector<>(); 不推荐使用,加了锁,影响性能
Collections.synchronizedList(new ArrayList<>());
new CopyOnWriteArrayList<>();

4.优化建议(同样的错误不犯第二次)

	 public boolean add(E e) {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] elements = getArray();
            int len = elements.length;
            Object[] newElements = Arrays.copyOf(elements, len + 1);
            newElements[len] = e;
            setArray(newElements);
            return true;
        } finally {
            lock.unlock();
        }
    }

在这里插入图片描述

5.公平锁、非公平锁、可重入锁、自旋锁谈谈你的理解?并手写一个自旋锁

  • 公平锁和非公平锁
    在这里插入图片描述
    区别在这里插入图片描述
  • 可重入锁(递归锁)
    指的是同一线程外层函数获得锁之后,内存够递归函数仍然能获取该锁的代码,在同一个线程在外层方法获取锁的时候,在进入内层方法会自动获取锁
    也即是说,线程可以进入任何一个它已经拥有的锁所同步着的代码块。
  • 自旋锁(spinlock)
    是指尝试获取的线程不会立即阻塞,而是采取循环的方式去尝试获取锁,这样的好处是减少线程上下文切换的消耗,缺点是循环会消耗CPU
    在这里插入图片描述
  • 手写自旋锁
    在这里插入图片描述
public class SpinLock {

    AtomicReference<Thread> atomicReference = new AtomicReference<>();

    public void  myLock(){
        Thread thread = Thread.currentThread();
        System.out.println(thread.getName()+"come in ");
        while (!atomicReference.compareAndSet(null,thread)){

        }
    }

    public void unLock(){
        Thread thread = Thread.currentThread();
        atomicReference.compareAndSet(thread,null);
        System.out.println(thread.getName()+"go out");
    }


    public static void main(String[] args) {

        SpinLock spinLock = new SpinLock();

        new Thread(()-> {
            spinLock.myLock();
            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            spinLock.unLock();
        },"A").start();
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(()-> {
            spinLock.myLock();
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            spinLock.unLock();
        },"B").start();
    }
}
  • 独占锁(写锁)、共享锁(读锁)、互斥锁
    在这里插入图片描述
    多个线程同时读一个资源类没有任何问题,所以为了满足并发量,读取共享资源应该可以同时进行。
    但是
    如果有一个线程想去写共享资源类,就不应该再有其他线程可以对该资源进行读或写
    总结:读-读能共存、读-写不能共存、写-写不能共存。
public static void main(String[] args) {
        MyLock myLock = new MyLock();

        for (int i = 1; i <= 5 ; i++) {
            final int tmpInt = i;
            new Thread(()->{
                myLock.put(String.valueOf(tmpInt),tmpInt);
            },String.valueOf(tmpInt)).start();

        }

//        try {
//            TimeUnit.SECONDS.sleep(1);
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }

        for (int i = 1; i <= 5 ; i++) {
            final int tmpInt = i;
            new Thread(()->{
                myLock.get(String.valueOf(tmpInt));
            },String.valueOf(tmpInt)).start();

        }

    }

}

class MyLock{

    private volatile Map<String,Object> mapCache = new HashMap();
    private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

    public void  put(String key,Object value){
        lock.writeLock().lock();
        try {
            System.out.println(Thread.currentThread().getName() + "开始写入:"+ key);
            try {TimeUnit.MILLISECONDS.sleep(300); } catch (InterruptedException e) {e.printStackTrace();}
            mapCache.put(key,value);
            System.out.println(Thread.currentThread().getName()+"写入完成");
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.writeLock().unlock();
        }



    }
    public void  get(String key){

        lock.readLock().lock();
        try {
            System.out.println(Thread.currentThread().getName() + "开始读取");
            try {TimeUnit.MILLISECONDS.sleep(300); } catch (InterruptedException e) {e.printStackTrace();}
            System.out.println(Thread.currentThread().getName()+"读取完成"+ mapCache.get(key));
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.readLock().unlock();
        }


    }

6.CountDownLatch/CyclicBarrier/Semaphore使用过吗?

  • countDowLatch
    班长最后走,关门
public static void main(String[] args) throws Exception {

        CountDownLatch countDownLatch = new CountDownLatch(10);

        for (int i = 0; i <10 ; i++) {
            new  Thread(()-> {
                System.out.println(Thread.currentThread().getName()+"离开教室");
                countDownLatch.countDown();
            },String.valueOf(i)).start();
        }

        countDownLatch.await();
        System.out.println(Thread.currentThread().getName()+"班长离开教室,锁门了");
    }

秦统一六国枚举类


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

        CountDownLatch countDownLatch = new CountDownLatch(6);

        for (int i = 1; i <=6 ; i++) {
            new  Thread(()-> {
                System.out.println(Thread.currentThread().getName()+"国,被灭");
                countDownLatch.countDown();
            },SixGuo.getGuo(i).reGuo).start();
        }

        countDownLatch.await();
        System.out.println(Thread.currentThread().getName()+"秦国一统天下");

        System.out.println(SixGuo.ONE);
        System.out.println(SixGuo.ONE.getReCode());
        System.out.println(SixGuo.ONE.getReGuo());
    }


    public enum  SixGuo{
        ONE(1,"齐"),TWO(2,"楚"),THREE(3,"燕"),FOUR(4,"赵"),FIVE(5,"魏"),SIX(6,"韩");

        private Integer reCode;
        private String reGuo;

        public Integer getReCode() {
            return reCode;
        }

        public String getReGuo() {
            return reGuo;
        }

        SixGuo(Integer reCode, String reGuo) {
            this.reCode = reCode;
            this.reGuo = reGuo;
        }

        public static SixGuo getGuo(int index){
            SixGuo[] sixGuos = SixGuo.values();
            for (SixGuo sixGuo : sixGuos){
                if (sixGuo.getReCode()==index) {
                    return sixGuo;
                }
            }
            return null;
        }
    }
  • CyclicBarrier
    和countdownlatch是相反的操作,可以理解为人到齐了再开会
CyclicBarrier cyclicBarrier = new CyclicBarrier(7, () -> {
            System.out.println("召唤神龙");
        });

        for (int i = 0; i <7 ; i++) {
            final int tmpInt = i;
            new Thread(()->{
                System.out.println("收集到第"+tmpInt+"颗");
                try {
                    cyclicBarrier.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            },String.valueOf(i)).start();
        }
  • Semaphore
    可以理解为多辆车抢车位,注定会有车没有车位
 public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(3);

        for (int i = 0; i <6 ; i++) {
            new Thread(()->{
                try {
                    semaphore.acquire();
                    System.out.println(Thread.currentThread().getName()+"抢占车位");
                    TimeUnit.SECONDS.sleep(3);
                    System.out.println(Thread.currentThread().getName()+"释放车位");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }finally {
                    semaphore.release();
                }
            },String.valueOf(i)).start();
        }
    }

7.阻塞队列知道吗?

在这里插入图片描述
blockingqueue的核心方法
在这里插入图片描述
在这里插入图片描述
SynchronousQueue
在这里插入图片描述

  • 用在哪里(多线程:高内聚低耦合场景下,线程操作资源类,多个操作一个要加锁。判断,干活,通知)
    1. 生产者和消费者
      初版生消
public class ProdConsummer_old {

    public static void main(String[] args) {

        changeData changeData = new changeData();

        new  Thread(()->{
            for (int i = 0; i <5 ; i++) {
                try {
                    changeData.increment();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        },"AAA").start();
        new  Thread(()->{
            for (int i = 0; i <5 ; i++) {
                try {
                    changeData.decrement();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        },"BBB").start();

    }
}

class changeData{
    private int number = 0;
    private Lock reentrantLock = new ReentrantLock();
    private Condition condition = reentrantLock.newCondition();

    public void increment() throws Exception{

        reentrantLock.lock();
        try {
            while (number != 0){
                condition.await();
            }
            number ++ ;
            System.out.println(Thread.currentThread().getName()+"\t"+ number);
            condition.signalAll();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            reentrantLock.unlock();
        }

    }

    public void decrement() throws Exception{

        reentrantLock.lock();
        try {
            while (number == 0){
                condition.await();
            }
            number -- ;
            System.out.println(Thread.currentThread().getName()+"\t"+ number);
            condition.signalAll();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            reentrantLock.unlock();
        }

    }

}

synchronized和lock有什么区别?用新的lock有什么好处?举例说说
1.原始构成
synchronized是关键字属于jvm层面,
monitorenter(底层是通过monitor对象来完成,其实wait、notify等方法也是依赖于monitor对象只有在同步块或方法中才能调wait、notify等方法)
monitorexit
lock是具体类(java.util.concurrent.Locks.lock)是api层面的锁
2.使用方法
synchronized不需要用户手动释放锁,当synchronized代码执行完后系统会自动让线程释放对锁的占用
reentrantlock则需要手动释放锁若没有主动释放锁,就有可能导致出现死锁现象(需要lock()和unlock()方法配合try、finally语句块来完成)
3.等待是否可中断
synchronized不可中断,除非抛出异常或者正常运行完成
reentrantlock可中断,
1.设置超时方法trylock(Long timeout ,TimeUtil unit)
2.lockInterruptibly()放代码块中,调用interrupt()方法可中断
4.加锁是否公平
synchronized非公平锁(非公平锁第一次未抢到资源,会变成公平锁去排队)
reentrantlock两者都可以,默认公平锁,构造方法可以传入Boolean值,true为公平锁,false为非公平锁
5.锁板顶多个条件condition
synchronized没有
reentrantlock用来实现分组唤醒需要唤醒的线程们,可以精确唤醒,而不是像synchronized要么随即唤醒一个线程要么唤醒全部线程。

8.线程池用过吗?ThreadPoolExcutor谈谈你的理解

  1. 为什么用线程池,优势
    在这里插入图片描述
  2. 线程池如何使用?
    在这里插入图片描述
    底层都是ThreadPoolExecutors
  3. 线程池的几个重要参数介绍?
    在这里插入图片描述
    在这里插入图片描述
    4.说说线程池的底层工作原理?
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

9.线程池用过吗?生产上你如何设置合理参数

  1. 是什么
    等待队列也已经满了,再也塞不下新任务了,同时,
    线城池中的max线城也达到了,无法继续为新任务服务。
    这个时候我们就需要拒绝策略机制合理的处理这个问题
  2. jdk内置的拒绝策略
    在这里插入图片描述
  3. 如何创建线程池,是否自定义过线程池
    在这里插入图片描述
    在这里插入图片描述
  4. 合理线程池你是如何考虑的?
    调用Runtime.getRuntime().availableProcessors();
    • cpu密集型
      在这里插入图片描述
    • IO密集型
      1. 由于IO密集型任务线程并不是一直在执行任务,则应配置尽可能多的线程,cpu数*2
      2. 在这里插入图片描述

10.死锁编码及定位分析

  1. 是什么
    在这里插入图片描述
    原因:系统资源不足、进程进行推进的顺序不合适、资源分配不当
  2. 代码
class  LockThread implements Runnable{
    private String lockA;
    private String lockB;
    public LockThread(String lockA, String lockB) {
        this.lockA = lockA;
        this.lockB = lockB;
    }

    @Override
    public void run() {
        synchronized (lockA){
            System.out.println(Thread.currentThread().getName()+"\t 持有"+lockA + "试图获取"+ lockB);
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (lockB){
                System.out.println(Thread.currentThread().getName()+"\t 持有"+lockB + "试图获取"+ lockA);
                try {
                    TimeUnit.SECONDS.sleep(2);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        }
    }
}


public class DeadLock {

    public static void main(String[] args) {
        String lockA = "lockA";
        String lockB = "lockB";

        new Thread(new LockThread(lockA,lockB),"AAAA").start();
        new Thread(new LockThread(lockB,lockA),"BBBB").start();
    }

}

调查死锁命令

1.jps(相当于linux系统下的ps -ef |grep XXXXX)是window下的java PS
定位进程号
2.jstack找到死锁查看
在这里插入图片描述
在这里插入图片描述
JVM相关

  1. JVM体系结构
    在这里插入图片描述

  2. GC作用域
    在这里插入图片描述

  3. 常见的垃圾回收算法
    引用计数
    在这里插入图片描述
    复制
    在这里插入图片描述

    标记清除
    在这里插入图片描述

    标记整理
    在这里插入图片描述

1. JVM垃圾回收的时候如何确定垃圾?是否知道什么是GC Roots

  1. 垃圾:简单说就是内存中不再被使用到的空间
  2. 要进行垃圾回收,如何判断一个对象是否可以被回收?
    1. 引用计数法,但是无法避免循环引用问题,所以不用这种方法
    2. 枚举根节点做可达性分析(根搜索路径)
      在这里插入图片描述
      在这里插入图片描述
  • 可以作为GC Roots的对象
    1. 虚拟机栈(栈帧中的局部变量区,也叫局部变量表)中引用的对象
    2. 方法区中的类静态属性引用的对象
    3. 方法区中常量引用的对象
    4. 本地方法栈中JNI(Native方法)引用的对象。

2.JVM调优和参数配置,如何查看盘点查看JVM系统默认值

  1. JVM参数类型
  • 标配参数
-version
-help
java -showversion
  • X参数(了解)
-Xint:解释执行
-Xcomp:第一次使用就编译成本地代码
-Xmixed:混合模式

在这里插入图片描述

  • XX参数
1.Boolean类型
公式:-XX:+ 或者- 某个属性值  (+ 表示开启,- 表示关闭)
举例:
	是否打印GC收集细节:-XX:-PrintGCDetails /-XX:+PrintGCDetails 
	是否使用串行垃圾回收器:-XX:-UserSerialGC/-XX:+UserSerialGC
2.KV设值类型
公式:-XX:属性key=属性值value
例子:
	-XX	:MetaspaceSize=128m
	-XX:MaxTenuringThreshold=15
3.jinfo举例,如何查看当前运行程序的配置
4.题外话(坑)
	两个经典参数:-Xms和-Xmx
	-Xms 等价于   -XX:InitialHeapSize
	-Xmx 等价于   -XX:MaxHeapSize

在这里插入图片描述

盘点参数家底
第一种 
	jps
	jinfo -flag 具体参数 java进程编号
	jinfo -flags               java进程编号
第二种
	java -xx:+PrintFlagsInitial  (查看初始值)
	java -xx:+PrintFlagsFinal  (查看最终值)

第三种
java -XX:+PrintCommandLineFlags 打印命令行参数

3.平时工作中常用的JVM基本参数

  1. 基础知识复习
    在这里插入图片描述
  2. 常用参数
    1. -Xms

      1. 初始大小内存,默认为物理内存1/64
      2. 等价于:-XX:InitialHeapSize
    2. -Xmx

      1. 最大分配内存,默认为物理内存1/4
      2. 等价于-XX:MaxHeapSize
    3. -Xss

      1. 设置单个线程栈的大小,一般默认为512k~1024k
      2. 等价于-XX:ThreadStackSize
      3. 查出来是0,表示用的是默认初始值
    4. -Xmn

      1. 设置年轻代大小(一般用默认就可以,不用调)
    5. -XX:MetaspaceSize

      1. 元空间的本质和永久代类似,都是对JVM规范中方法区的实现。不过元空间与永久代之间最大的区别在于:**元空间并不在虚拟机中,而是使用本地内存。**因此,默认情况下,元空间的大小仅受本地内存限制。
      2. -Xms10m -Xmx10m -XX:MetaspaceSize=1024m -XX:+PrintFlagsFinal
    6. -XX:+PrintGCDetails

      1. 输出详细GC手机日志信息

      2. GC 在这里插入图片描述

      3. FullGC
        在这里插入图片描述

    7. -XX:SurvivorRatio
      在这里插入图片描述

      1. 设置新生代中eden和s0和s1空间的比例,默认-XX:SurvivoRatio8,Eden:s0:s1=8:1:1
      2. 加入 -XX:SurvivorRatio=4,Eden:s0:s1=4:1:1
      3. SurvivoRatio值就是设置eden区的比例占多少
    8. -XX:NewRatio

      1. 配置年轻代与老年代在堆结构的占比,默认-XX:NewRatio=2新生代占1,老年代2,年轻代占整个堆的1/3
      2. 假如 -XX:NewRatio=4 新生代占1,老年代4,年轻代占整个堆的1/5 NewRatio值就是设置老年代的占比,剩下的1给新生代
    9. -XX:MaxTenuringThreshold

      1. 设置垃圾的最大年龄,范围是从1-15,默认是15

4.强引用、软引用、弱引用、虚引用分别是什么?

  1. 整体架构 在这里插入图片描述
  2. 强引用(默认支持模式) 在这里插入图片描述
    在这里插入图片描述
  3. 软引用 在这里插入图片描述
    在这里插入图片描述
  4. 弱引用
    弱引用需要用java.lang.WeakReference类来实现,它比软引用的生存期更短,对于只有弱引用的对象来说,只要垃圾回收机制一运行,㕆JVM的内存空间是否足够,都会回收该对象占用的内存。 在这里插入图片描述
应用场景:
	假如有一个应用需要读取大量的本地图片:
		* 如果每次读取图片都从硬盘读取则会严重影响性能,
		* 如果一次性全部加载到内存中又可能造成内存溢出
	此时使用软引用或者弱引用可以解决这个问题
		设计思路:用一个hashMap来保存图片的路径和相应图片对象关联的软引用之间的映射关系,在内存不足时,JVM会自动回收这些缓存图片对象所占用的空间,从而有效地避免了OOM的问题
	Map<String,SoftReference<Bitmap>> imageCache = new HashMap<String,SoftReference<Bitmap>>();	
	

weakhashmap案例演示和分析
在这里插入图片描述
在这里插入图片描述

  1. 虚引用 在这里插入图片描述
    GCroot和四种引用总结
    在这里插入图片描述

5.谈谈对OOM的认识

在这里插入图片描述

  1. java.lang.StackOverflowError(栈管运行,堆管对象)
    深度方法调用(递归的调用方法,但是没有结束标识)
  2. java.lang.OutOfMemoryError:Java heap space
    创建大对象,new byte[8010241024]
  3. java.lang.OutOfMemoryError:GC overhead limit exceeded
    在这里插入图片描述
  4. java.lang.OutOfMemoryError:unable to create new native thread 在这里插入图片描述
    在这里插入图片描述
    调整生成线程上限
    在这里插入图片描述
    在这里插入图片描述
  5. java.lang.OutOfMemoryError:Metaspace
    在这里插入图片描述

6.GC垃圾回收算法和垃圾收集器的关系?分别是什么?

  1. GC算法(引用计数、复制、标记清理、标记整理或标记压缩)是内存回收的方法论,垃圾收集器就是方法论的落地。
  2. 因为目前为止还没有完美的收集器出现,更加没有万能的收集器,只是针对具体应用最合适的收集器,进行分代收集
  3. 四种主要的垃圾收集器
    串行、并行、并发、G1
    在这里插入图片描述
    • 串行垃圾回收器(Serial):它为单线程环境设计且只是用一个线程进行垃圾回收,会暂停所有的用户线程。所以不适合服务器环境
    • 并行垃圾回收器(Parallel):多个垃圾收集线程并行工作,此时用户线程是暂停的,适用于科学计算、大数据处理 等弱交互场景
    • 并发垃圾回收器(CMS):用户线程和垃圾收集线程同时执行(不一定是并行,可能交替执行),不需要停顿用户线程互联网公司多用它,适用对响应时间有要求的场景 在这里插入图片描述
    • G1垃圾回收器:G1垃圾回收器将堆内存分割成不同的区域然后并发的对其进行垃圾回收

7.怎么查看服务器默认的垃圾收集器是哪个?生产上如何配置垃圾收集器?谈谈你对垃圾收集器的理解?

  1. 查看方式 在这里插入图片描述
  2. 有哪些GC垃圾收集器 在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
  3. 部分参数预先说明
    • DefNew:Default New Generation
    • Tenured:Old
    • ParNew:Parallel New Generation
    • PSYongGen:Parallel Scavenge
    • ParOldGen:Parallel Old Generation
  4. server/client模式
    在这里插入图片描述
  5. 新生代
    • 串行GC(serial)/(serial Copying) 在这里插入图片描述
      在这里插入图片描述

    • 并行GC(ParNew) 在这里插入图片描述
      在这里插入图片描述

    • 并行回收GC(Parallel)/(Parallel Scavenge) 在这里插入图片描述
      在这里插入图片描述

  6. 老年代
    • 串行GC(Serial Old)/(serial MSC) 在这里插入图片描述

    • 并行GC(Paraller Old)/(Parallel MSC) 在这里插入图片描述

    • 并发标记清除GC(CMS)

  7. 如何选择垃圾收集器
    在这里插入图片描述
    在这里插入图片描述

8.G1垃圾收集器

  1. G1是什么
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
  2. 特点 在这里插入图片描述
  3. 底层原理
    • region区域垃圾收集器
      最大的好处是化整为零,避免全内存扫描,只需要按照区域进行扫描即可
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述

    • 回收步骤
      在这里插入图片描述
      在这里插入图片描述

    • 4步过程
      在这里插入图片描述

  • 和cms相比的优势
    在这里插入图片描述

9.生产环境服务器慢,诊断思路和性能评估谈一谈?

  1. 整机:top
    主要看两点,一时cup和内存,而是看load average(一分钟,5分钟,10分钟的平均值,需要三个数相加求平均值,如果大于60%就是负责过大)
    精简版命令 uptimes

  2. cpu:vmstat
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

  3. 内存:free
    在这里插入图片描述
    在这里插入图片描述

  4. 硬盘:df
    df -h (h是以人类看得懂的方式打印出来,既以G为单位)

  5. 磁盘IO:iostat
    在这里插入图片描述
    在这里插入图片描述

  6. 网络IO:ifstat
    在这里插入图片描述

10.假如生产环境出现cpu占用过高,请你谈谈分析思路和定位

  1. 结合linux和jdk命令一起分析
    1. 先用top命令找出cpu占比做高的

    2. ps -ef 或jps进一步定位,得知是一个怎样的后台程序造成

    3. 定位到具体线程或者代码
      在这里插入图片描述
      在这里插入图片描述

    4. 将需要的线程ID装换成16进制格式(英文小写格式)
      printf"%x\n" 有问题的线程id

    5. jstack进程ID | grep tid(16进制线程小写英文) -A60

  2. 案例步骤

11.GITHUB

  1. in
    XX关键词 in:name或description或readme

  2. stars/fork
    springboot stars:>=5000
    springboot fork:>=5000
    在这里插入图片描述

  3. awesome加强搜索
    公式:awesome 关键字 (awesome系列一般是用来收集学习、工作、书籍类相关的项目)

  4. 高亮显示某一行代码
    在

  5. 项目内搜索
    在这里插入图片描述

  6. 搜索某个地区内的大佬
    在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值