并发与并行之间的区别? 并发相当于 多个线程来秒杀 并行 一边 吃饭 一边看电视
你吃饭吃到一半,电话来了,你一直到吃完了以后才去接,这就说明你不支持并发也不支持并行。你吃饭吃到一半,电话来了,你停了下来接了电话,接完后继续吃饭,这说明你支持并发。你吃饭吃到一半,电话来了,你一边打电话一边吃饭,这说明你支持并行。并发的关键是你有处理多个任务的能力,不一定要同时。并行的关键是你有同时处理多个任务的能力。所以我认为它们最关键的点就是:是否是『同时』。
链接:https://www.zhihu.com/question/33515481/answer/58849148
https://www.zhihu.com/question/33515481
并发,指的是多个事情,在同一时间段内同时发生了。 并行,指的是多个事情,在同一时间点上同时发生了https://cloud.tencent.com/developer/article/1424249
Lock 与 Synchronized 之间区别? Lock 是接口 Synchronized 是关键字 自动和手动控制锁
volatile 是什么? volatile 是 Java 虚拟机提供的轻量级的同步机制 保证可见性, 不保证原子性, 禁止指令重排
画出JVM内存模型
JMM内存模型 JMM 可见性, 原子性, 有序性 但是没有volatile是没有原子性的
本地内存就是工作内存
volatile 可见性实现 当 int number前面没有volatile关键字时, main线程会一直在while死循环中, myData.number的值没有得到及时更新 当有volatile的时候, 当number的值 , 在其他的线程工作内存修改之后, 就会拷贝回主内存, 并且其他线程是可见的, while不会死循环
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 package com.atguigu.thread;import java.util.concurrent.TimeUnit;class MyData { volatile int number = 0 ; public void addTo60 () { this .number = 60 ; } }public class VolatileDemo { public static void main (String[] args) { MyData myData = new MyData (); new Thread (() -> { System.out.println(Thread.currentThread().getName() + "\tcome in" ); try { TimeUnit.SECONDS.sleep(3 ); } catch (InterruptedException e) { e.printStackTrace(); } myData.addTo60(); System.out.println(Thread.currentThread().getName() + "\t update number value: " + myData.number); }, "AAA" ).start(); while (myData.number == 0 ) { } System.out.println(Thread.currentThread().getName() + "\t mission is over" ); } }
存在volatile运行结果
1 2 3 AAA come in AAA update number value : 60 main mission is over
配置javap插件 参考: https://blog.csdn.net/stormkai/article/details/120079797
通过idea配置javap -c命令来配置下javap命令 选择File->Settings->Tools->External Tools 点击+号
1 2 3 Program:$ JDKPath$ \bin\javap.exe Arguments:-c $ OutputPath$ \$ FileDirRelativeToSourcepath$ \$ FileNameWithoutAllExtensions$ .class Working directory:$ ProjectFileDir$
volatile借助原子类AtomicInteger解决可见性 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 package com.atguigu.thread;import java.util.concurrent.TimeUnit;import java.util.concurrent.atomic.AtomicInteger;class MyData { volatile int number = 0 ; public void addTo60 () { this .number = 60 ; } public void addPlusPlus () { number++; } AtomicInteger atomicInteger = new AtomicInteger (); public void addMyAtomic () { atomicInteger.getAndIncrement(); } }public class VolatileDemo { public static void main (String[] args) { MyData myData = new MyData (); for (int i = 1 ; i <= 20 ; i++) { new Thread (() -> { for (int j = 1 ; j <= 1000 ; j++) { myData.addPlusPlus(); myData.addMyAtomic(); } }, String.valueOf(i)).start(); } while (Thread.activeCount() > 2 ) { Thread.yield (); } System.out.println(Thread.currentThread().getName() + "\t int type, finally number value: " + myData.number); System.out.println(Thread.currentThread().getName() + "\t AtomicInteger type finally number value: " + myData.atomicInteger); } private static void seeOKByVolatile () { MyData myData = new MyData (); new Thread (() -> { System.out.println(Thread.currentThread().getName() + "\tcome in" ); try { TimeUnit.SECONDS.sleep(3 ); } catch (InterruptedException e) { e.printStackTrace(); } myData.addTo60(); System.out.println(Thread.currentThread().getName() + "\t update number value: " + myData.number); }, "AAA" ).start(); while (myData.number == 0 ) { } System.out.println(Thread.currentThread().getName() + "\t mission is over" ); } }
指令重排 多线程环境中线程交替执行, 由于编译器优化重排的存在, 两个线程中使用的变量能否保证一致性是无法确定的, 结果无法预测
总结: 工作内存与主内存同步延迟现象导致的可见性问题 可以使用synchronized或volatile关键字解决, 它们都可以使一个线程修改后的变量立即对其他线程可见
对于指令重排导致的可见性问题和有序性问题 可以利用volatile关键字解决, 因为volatile的另外一个作用就是禁止重排序优化
单例模式-synchronized 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 package com.atguigu.thread;public class SingletonDemo { private static SingletonDemo instance; private SingletonDemo () { System.out.println(Thread.currentThread().getName() + "\t构造方法SingletonDemo()" ); } public synchronized static SingletonDemo getInstance () { if (instance == null ) { instance = new SingletonDemo (); } return instance; } public static void main (String[] args) { for (int i = 1 ; i <= 10 ; i++) { new Thread (() -> { SingletonDemo.getInstance(); }, String.valueOf(i)).start(); } } }
单例模式-volatile 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 package com.atguigu.thread;public class SingletonDemo { private volatile static SingletonDemo instance; private SingletonDemo () { System.out.println(Thread.currentThread().getName() + "\t构造方法SingletonDemo()" ); } public static SingletonDemo getInstance () { if (instance == null ) { synchronized (SingletonDemo.class) { if (instance == null ) { instance = new SingletonDemo (); } } } return instance; } public static void main (String[] args) { for (int i = 1 ; i <= 10 ; i++) { new Thread (() -> { SingletonDemo.getInstance(); }, String.valueOf(i)).start(); } } }
CAS 比较并交换
真实值与期望值相比较, 如果相同 , 修改值, 如果不同 , 不修改值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 package com.atguigu.thread;import java.util.concurrent.atomic.AtomicInteger;public class CASDemo { public static void main (String[] args) { AtomicInteger atomicInteger = new AtomicInteger (5 ); System.out.println(atomicInteger.compareAndSet(5 , 2019 ) + "\t current data: " + atomicInteger.get()); System.out.println(atomicInteger.compareAndSet(5 , 1024 ) + "\t current data: " + atomicInteger.get()); } }
atomicInteger底层源码 CAS 和 Unsafe类(Unsafe中的方法大部分都是native的)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 atomicInteger.getAndIncrement()public final int getAndIncrement () { return unsafe.getAndAddInt(this , valueOffset, 1 ); }public final int getAndAddInt (Object var1, long var2, int var4) { int var5; do { var5 = this .getIntVolatile(var1, var2); } while (!this .compareAndSwapInt(var1, var2, var5, var5 + var4)); return var5; }
CAS总结(CompareAndSwap): 比较当前工作内存中的和主内存中的值, 如果相同则执行规定操作, 否则继续比较知道主内存和工作内存中的值一致为止
CAS应用: CAS有3个操作数, 内存值V, 旧的预期值A, 要修改的更新至B 当且仅当预期值A和内存值V相同时, 将内存值V修改为B, 否则什么都不做
CAS缺点 循环时间长开销很大 只能保证一个共享变量的原子操作 引出来ABA问题
ABA问题 CAS会导致”ABA问题” CAS算法实现一个重要前提需要去除内存中某时刻的数据并在当下时刻比较并替换,那么在这个时间差类会导致数据的变化。
比如说一个线程one从内存位置V中取出A,这时候另一个线程two也从内存中取出A,并且线程two进行了一些操作将值变成了B. 然后线程two又将V位置的数据变成A,这时候线程one进行CAS操作发现内存中仍然是A,然后线程one操作成功。
尽管线程one的CAS操作成功,但是不代表这个过程就是没有问题的。
原子引用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package com.atguigu.thread;import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;import java.util.concurrent.atomic.AtomicReference;@Data @AllArgsConstructor @NoArgsConstructor class User { String userName; int age; }public class AtomicReferenceDemo { public static void main (String[] args) { User z3 = new User ("z3" , 22 ); User li4 = new User ("li4" , 25 ); AtomicReference<User> atomicReference = new AtomicReference <>(); atomicReference.set(z3); System.out.println(atomicReference.compareAndSet(z3, li4) + "\t" + atomicReference.get().toString()); System.out.println(atomicReference.compareAndSet(z3, li4) + "\t" + atomicReference.get().toString()); } }
如何解决ABA问题? 如何解决ABA问题, 使用版本号(时间戳stamp)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 package com.atguigu.thread;import java.util.concurrent.TimeUnit;import java.util.concurrent.atomic.AtomicReference;import java.util.concurrent.atomic.AtomicStampedReference;public class ABADemo { static AtomicReference<Integer> atomicReference = new AtomicReference <>(100 ); static AtomicStampedReference<Integer> atomicStampedReference = new AtomicStampedReference <>(100 , 1 ); public static void main (String[] args) { System.out.println("========== 以下是ABA问题的产生 ==========" ); new Thread (() -> { atomicReference.compareAndSet(100 , 101 ); atomicReference.compareAndSet(101 , 100 ); }, "t1" ).start(); new Thread (() -> { try { TimeUnit.SECONDS.sleep(1 ); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(atomicReference.compareAndSet(100 , 2019 ) + "\t" + atomicReference.get()); }, "t2" ).start(); try { TimeUnit.SECONDS.sleep(2 ); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("========== 以下是ABA问题的解决 ==========" ); new Thread (() -> { int stamp = atomicStampedReference.getStamp(); System.out.println(Thread.currentThread().getName() + "\t" + "第 1 次版本号: " + stamp); try { TimeUnit.SECONDS.sleep(1 ); } catch (InterruptedException e) { e.printStackTrace(); } atomicStampedReference.compareAndSet(100 , 101 , stamp, atomicStampedReference.getStamp()+1 ); System.out.println(Thread.currentThread().getName() + "\t" + "第 2 次版本号: " + atomicStampedReference.getStamp()); atomicStampedReference.compareAndSet(101 , 100 , atomicStampedReference.getStamp(), atomicStampedReference.getStamp()+1 ); System.out.println(Thread.currentThread().getName() + "\t" + "第 3 次版本号: " + atomicStampedReference.getStamp()); }, "t3" ).start(); new Thread (() -> { int stamp = atomicStampedReference.getStamp(); System.out.println(Thread.currentThread().getName() + "\t" + "第 1 次版本号: " + stamp); try { TimeUnit.SECONDS.sleep(3 ); } catch (InterruptedException e) { e.printStackTrace(); } boolean b = atomicStampedReference.compareAndSet(100 , 2024 , stamp, atomicStampedReference.getStamp() + 1 ); System.out.println(Thread.currentThread().getName() + "\t" + "修改成功否: " + b + "当前最新实际版本号: " + atomicStampedReference.getStamp()); System.out.println(Thread.currentThread().getName() + "\t" + "当前实际最新值: " + atomicStampedReference.getReference()); }, "t4" ).start(); } }
运行结果:
ArrayList容器不安全? 以及保证List安全 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 package com.atguigu.thread;import java.util.*;import java.util.concurrent.CopyOnWriteArrayList;public class ContainerNotSafeDemo { public static void main (String[] args) { List<String> list = new CopyOnWriteArrayList <>(); for (int i = 1 ; i <= 30 ; i++) { new Thread (() -> { list.add(UUID.randomUUID().toString().substring(0 , 8 )); System.out.println(list); }, String.valueOf(i)).start(); } } }
拓展 set map
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 package com.atguigu.thread;import java.util.*;import java.util.concurrent.ConcurrentHashMap;import java.util.concurrent.CopyOnWriteArrayList;import java.util.concurrent.CopyOnWriteArraySet;public class ContainerNotSafeDemo { public static void main (String[] args) { Map<String, String> map = Collections.synchronizedMap(new HashMap <>()); for (int i = 1 ; i <= 30 ; i++) { new Thread (() -> { map.put( Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0 , 8 )); System.out.println(map); }, String.valueOf(i)).start(); } } private static void setNotSafe () { Set <String> set = new CopyOnWriteArraySet <>(); for (int i = 1 ; i <= 30 ; i++) { new Thread (() -> { set.add(UUID.randomUUID().toString().substring(0 , 8 )); System.out.println(set); }, String.valueOf(i)).start(); } new HashSet <Integer>().add(1 ); } }
一道传参练习题 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 package com.atguigu.thread;import lombok.Data;@Data class Person { private Integer id; private String personName; public Person (String personName) { this .personName = personName; } }public class TestTransferValue { public void changeValue1 (int age) { age = 30 ; } public void changeValue2 (Person person) { person.setPersonName("xxx" ); } public void changeValue3 (String str) { str = "xxx" ; } public static void main (String[] args) { TestTransferValue test = new TestTransferValue (); int age = 20 ; test.changeValue1(age); System.out.println("age = " + age); Person person = new Person ("abc" ); test.changeValue2(person); System.out.println("person = " + person); String str = "abc" ; test.changeValue3(str); System.out.println("str = " + str); } }
公平锁与非公平锁 公平锁 是指多个线程按照申请锁的顺序来获取锁,类似排队打饭,先来后到。 非公平锁 是指多个线程获取锁的顺序并不是按照中请锁的顺序,有可能后中请的线程比先中请的线程优先获取锁 在高并发的情况下,有可能会造成优先级反转或者饥饿现象
Java ReentrantLock而言 通过构造函数指定该锁是否是公平锁, 默认就是非公平锁. 非公平锁的优点在于吞吐量比公平锁大
对于Synchronized而言, 也是一种非公平锁
可重入锁(又名递归锁) 可重入锁(又名递归锁)
指的是同一线程外层函数获得锁之后 ,内层递归函数仍然能获取该锁的代码, 在同一个线程在外层方法获取锁的时候,在进入内层方法会自动获取锁 也即是说,线程可以进入任何一个它已经拥有的锁所同步着的代码块。
锁中内部还有一把锁
ReentrantLock/Synchronized就是一个典型的可重入锁 可重入锁最大的作用就是避免死锁
java锁之可重入锁和递归锁代码验证
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 package com.atguigu.thread;import java.util.concurrent.TimeUnit;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;class Phone implements Runnable { public synchronized void sendSMS () { System.out.println(Thread.currentThread().getName() + "\t sendSMS()" ); sendEmail(); } public synchronized void sendEmail () { System.out.println(Thread.currentThread().getName() + "\t sendEmail()" ); } @Override public void run () { get(); } Lock lock = new ReentrantLock (); private void get () { lock.lock(); lock.lock(); lock.lock(); lock.lock(); lock.lock(); try { System.out.println(Thread.currentThread().getName() + "\t get()" ); set(); } finally { lock.unlock(); lock.unlock(); lock.unlock(); lock.unlock(); lock.unlock(); } } private void set () { lock.lock(); try { System.out.println(Thread.currentThread().getName() + "\t set()" ); } finally { lock.unlock(); } } }public class ReenterLockDemo { public static void main (String[] args) { Phone phone = new Phone (); new Thread (() -> { phone.sendSMS(); }, "t1" ).start(); new Thread (() -> { phone.sendSMS(); }, "t2" ).start(); try { TimeUnit.SECONDS.sleep(2 ); System.out.println(); System.out.println(); System.out.println(); } catch (InterruptedException e) { e.printStackTrace(); } Thread t3 = new Thread (phone, "t3" ); t3.start(); Thread t4 = new Thread (phone, "t4" ); t4.start(); } }
自旋锁 (spinlock) 是指尝试获取锁的线程不会立即阳塞,而是采用循环的方式去尝试获取锁,这样的好处是减少线程上下文切换的消耗,缺 点是循环会消耗CPU
手写自旋锁
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 package com.atguigu.thread;import java.util.concurrent.TimeUnit;import java.util.concurrent.atomic.AtomicReference;public class SpinLockDemo { AtomicReference<Thread> atomicReference = new AtomicReference <>(); public void myLock () { Thread thread = Thread.currentThread(); System.out.println(Thread.currentThread().getName() + "\t myLock" ); while (!atomicReference.compareAndSet(null , thread)) { } } public void myUnLock () { Thread thread = Thread.currentThread(); System.out.println(Thread.currentThread().getName() + "\t myUnLock" ); atomicReference.compareAndSet(thread, null ); } public static void main (String[] args) { SpinLockDemo spinLockDemo = new SpinLockDemo (); new Thread (() -> { spinLockDemo.myLock(); try { TimeUnit.SECONDS.sleep(5 ); } catch (InterruptedException e) { throw new RuntimeException (e); } spinLockDemo.myUnLock(); }, "t1" ).start(); try { TimeUnit.SECONDS.sleep(2 ); } catch (InterruptedException e) { throw new RuntimeException (e); } new Thread (() -> { spinLockDemo.myLock(); try { TimeUnit.SECONDS.sleep(2 ); } catch (InterruptedException e) { throw new RuntimeException (e); } spinLockDemo.myUnLock(); }, "t2" ).start(); } }
读写锁 独占锁: 指该锁一次只能被一个线程所持有。对ReentrantLock和Synchronized而言都是独占锁 共享锁:指该锁可被多个线程所持有。 对ReentrantReadWriteLock其读锁是共享锁,其写锁是独占锁。 读锁的共享锁可保证并发读是非常高效的,读写,写读 ,写写的过程是互斥的。
未使用读写锁暴露出来的问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 package com.atguigu.thread;import java.util.HashMap;import java.util.Map;class MyCache { private volatile Map<String, Object> map = new HashMap <>(); public void put (String key, Object value) { System.out.println(Thread.currentThread().getName() + "\t 正在写入: " + key); map.put(key, value); System.out.println(Thread.currentThread().getName() + "\t 写入完成" ); } public void get (String key) { System.out.println(Thread.currentThread().getName() + "\t 正在读取" ); Object result = map.get(key); System.out.println(Thread.currentThread().getName() + "\t 读取完成: " + result); } public void clear () { map.clear(); } }public class ReadWriteLockDemo { public static void main (String[] args) { MyCache mycache = new MyCache (); for (int i = 1 ; i <= 5 ; i++) { final int finalInt = i; new Thread (() -> { mycache.put(finalInt + "" , finalInt + "" ); }, String.valueOf(i)).start(); } for (int i = 1 ; i <= 5 ; i++) { final int finalInt = i; new Thread (() -> { mycache.get(finalInt + "" ); }, String.valueOf(i)).start(); } } }
CountDownLatch 让一些线程阻塞直到另一些线程完成一系列操作后才被唤醒 CountDownLatch主要有两个方法,当一个或多个线程调用await方法时,调用线程会被阻塞 其它线程调用countDown方法会将计数器减1(调用countDown方法的线程不会阻塞), 当计数器的值变为零时,因调用await方法被阻塞的线程会被唤醒,继续执行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 package com.atguigu.thread;import lombok.Getter;import java.util.concurrent.CountDownLatch;enum CountryEnum { ONE(1 , "齐" ), TWO(2 , "楚" ), THREE(3 , "燕" ), FOUR(4 , "赵" ), FIVE(5 , "魏" ), SIX(6 , "韩" ); @Getter private Integer retCode; @Getter private String retMessage; CountryEnum(Integer retCode, String retMessage) { this .retCode = retCode; this .retMessage = retMessage; } public static CountryEnum forEach_CountryEnum (int index) { for (CountryEnum value : CountryEnum.values()) { if (value.getRetCode() == index) { return value; } } return null ; } }public class CountDownLatchDemo { public static void main (String[] args) throws InterruptedException { CountDownLatch countDownLatch = new CountDownLatch (6 ); for (int i = 1 ; i <= 6 ; i++) { new Thread (() -> { System.out.println(Thread.currentThread().getName() + "国, 被灭" ); countDownLatch.countDown(); }, CountryEnum.forEach_CountryEnum(i).getRetMessage()).start(); } countDownLatch.await(); System.out.println(Thread.currentThread().getName() + "秦帝国, 一统华夏" ); System.out.println(); System.out.println(CountryEnum.ONE); System.out.println(CountryEnum.ONE.getRetCode()); System.out.println(CountryEnum.ONE.getRetMessage()); } private static void closeDoor () throws InterruptedException { CountDownLatch countDownLatch = new CountDownLatch (6 ); for (int i = 1 ; i <= 6 ; i++) { new Thread (() -> { System.out.println(Thread.currentThread().getName() + "\t上完晚自习, 离开教室" ); countDownLatch.countDown(); }, String.valueOf(i)).start(); } countDownLatch.await(); System.out.println(Thread.currentThread().getName() + "班长最后关门走人" ); } }
CyclicBarrier CyclicBarrier Cyclic循环 Barrier屏障
CyclicBarrier的字面意思是可循环 (Cyclic) 使用的屏障 (Barrier)。 它要做的事情是,让一组线程到达一个屏障(也可以叫同步点) 时被阻塞,直到最后一个线程到达屏障时, 屏障才会开门,所有被屏障拦截的线程才会继续干活,线程进入屏障通过CyclicBarrier的await()方法
集齐7颗龙珠就能召唤神龙
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 package com.atguigu.thread;import java.util.concurrent.BrokenBarrierException;import java.util.concurrent.CyclicBarrier;public class CyclicBarrierDemo { public static void main (String[] args) { CyclicBarrier cyclicBarrier = new CyclicBarrier (7 , () -> { System.out.println("召唤神龙" ); }); for (int i = 1 ; i <= 7 ; i++) { new Thread (() -> { System.out.println("收集到第:" + Thread.currentThread().getName() + "\t龙珠" ); try { cyclicBarrier.await(); } catch (InterruptedException e) { throw new RuntimeException (e); } catch (BrokenBarrierException e) { throw new RuntimeException (e); } }, String.valueOf(i)).start(); } } }
Semaphore Semaphore 信号量 , 信号灯
信号量主要用于两个目的, 一个是用于多个共享资源的互斥使用, 另一个用于并发线程数的控制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 package com.atguigu.thread;import java.util.concurrent.Semaphore;import java.util.concurrent.TimeUnit;public class SemaphoreDemo { public static void main (String[] args) { Semaphore semaphore = new Semaphore (3 ); for (int i = 1 ; i <= 6 ; i++) { new Thread (() -> { try { semaphore.acquire(); System.out.println(Thread.currentThread().getName() + "\t抢到车位" ); try { TimeUnit.SECONDS.sleep(3 ); } catch (InterruptedException e) { throw new RuntimeException (e); } System.out.println(Thread.currentThread().getName() + "\t停车3秒后离开车位" ); } catch (InterruptedException e) { throw new RuntimeException (e); } finally { semaphore.release(); } }, String.valueOf(i)).start(); } } }
BlockQueue 阻塞队列
*试图从空的阻塞队列中获取元素的线程将会被阻塞,直到其他的线程往空的队列插入新的元素。 同样 试图往已满的阻寨队列中添加新元素的线程同样也会被阻寨,直到其他的线程从列中移除一个或者多个元素或者完全清空队 列后使队列重新变得空闲起来并后续新增
在多线程领域: 所谓阻塞,在某些情况下会挂起线程(即阻塞),一旦条件满足,被挂起的线程又会自动被唤醒
为什么需要BlockingQueue 好处是我们不需要关心什么时候需要阻塞线程,什么时候需要唤醒线程,因为这一切BlockingQueue都给你一手包办了
在concurrent包发布以前,在多线程环境下,我们每个程序员都必须去自己控制这些细节,尤其还要兼顾效率和线程安全,而这会给我们的程序带来不小的复杂度。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 package com.atguigu.thread;import java.util.ArrayList;import java.util.List;import java.util.concurrent.ArrayBlockingQueue;import java.util.concurrent.BlockingQueue;import java.util.concurrent.TimeUnit;public class BlockingQueueDemo { public static void main (String[] args) throws InterruptedException { BlockingQueue<String> blockingQueue = new ArrayBlockingQueue <>(3 ); System.out.println(blockingQueue.offer("a" , 2L , TimeUnit.SECONDS)); System.out.println(blockingQueue.offer("a" , 2L , TimeUnit.SECONDS)); System.out.println(blockingQueue.offer("a" , 2L , TimeUnit.SECONDS)); } }
SynchronousQueue 阻塞队列之同步SynchronousQueue队列
SynchronousQueue没有容量 与其他BlockingQueue不同, SynchronousQueue是一个不存储元素的BlockingQueue 每一个put操作必须要等待一个take操作, 否则不能继续添加元素, 反之亦然
synchronousQueue 生产一个阻塞等待消费一个, 并不会存储第二个, 只有第一个消费掉了, 才会开始生产第二个
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 package com.atguigu.thread;import java.util.concurrent.SynchronousQueue;import java.util.concurrent.TimeUnit;public class SynchronousQueueDemo { public static void main (String[] args) { SynchronousQueue<String> synchronousQueue = new SynchronousQueue <>(); new Thread (() -> { try { System.out.println(Thread.currentThread().getName() + "\t put 1" ); synchronousQueue.put("1" ); System.out.println(Thread.currentThread().getName() + "\t put 2" ); synchronousQueue.put("2" ); System.out.println(Thread.currentThread().getName() + "\t put 3" ); synchronousQueue.put("3" ); } catch (InterruptedException e) { throw new RuntimeException (e); } }, "AAA" ).start(); new Thread (() -> { try { TimeUnit.SECONDS.sleep(5 ); System.out.println(Thread.currentThread().getName() + "\t get" + synchronousQueue.take()); TimeUnit.SECONDS.sleep(5 ); System.out.println(Thread.currentThread().getName() + "\t get" + synchronousQueue.take()); TimeUnit.SECONDS.sleep(5 ); System.out.println(Thread.currentThread().getName() + "\t get" + synchronousQueue.take()); } catch (InterruptedException e) { throw new RuntimeException (e); } }, "BBB" ).start(); } }
线程通信之生产者消费者传统版 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 package com.atguigu.thread;import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;class ShareData { private int number = 0 ; private Lock lock = new ReentrantLock (); private Condition condition = lock.newCondition(); public void increment () throws Exception { lock.lock(); try { while (number != 0 ) { condition.await(); } number++; System.out.println(Thread.currentThread().getName() + "\t" + number); condition.signalAll(); } finally { lock.unlock(); } } public void decrement () throws Exception { lock.lock(); try { while (number == 0 ) { condition.await(); } number--; System.out.println(Thread.currentThread().getName() + "\t" + number); condition.signalAll(); } finally { lock.unlock(); } } }public class ProdConsumer_TraditionDemo { public static void main (String[] args) { ShareData shareData = new ShareData (); new Thread (() -> { for (int i = 1 ; i <= 5 ; i++) { try { shareData.increment(); } catch (Exception e) { throw new RuntimeException (e); } } }, "AA" ).start(); new Thread (() -> { for (int i = 1 ; i <= 5 ; i++) { try { shareData.decrement(); } catch (Exception e) { throw new RuntimeException (e); } } }, "BB" ).start(); } }
wait方法是什么类的? Object
Synchronized和Lock有什么区别 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 package com.atguigu.thread;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;public class SyncAndReentrantLockDemo { public static void main (String[] args) { synchronized (new Object ()) { } Lock lock = new ReentrantLock (); } }
锁绑定多个条件Condition 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 package com.atguigu.thread;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;class ShareResource { private int number = 1 ; private Lock lock = new ReentrantLock (); Condition c1 = lock.newCondition(); Condition c2 = lock.newCondition(); Condition c3 = lock.newCondition(); public void print5 () { lock.lock(); try { while (number != 1 ) { try { c1.await(); } catch (InterruptedException e) { throw new RuntimeException (e); } } for (int i = 1 ; i <= 5 ; i++) { System.out.println(Thread.currentThread().getName() + "\t" + i); } number = 2 ; c2.signal(); } finally { lock.unlock(); } } public void print10 () { lock.lock(); try { while (number != 2 ) { try { c2.await(); } catch (InterruptedException e) { throw new RuntimeException (e); } } for (int i = 1 ; i <= 10 ; i++) { System.out.println(Thread.currentThread().getName() + "\t" + i); } number = 3 ; c3.signal(); } finally { lock.unlock(); } } public void print15 () { lock.lock(); try { while (number != 3 ) { try { c3.await(); } catch (InterruptedException e) { throw new RuntimeException (e); } } for (int i = 1 ; i <= 15 ; i++) { System.out.println(Thread.currentThread().getName() + "\t" + i); } number = 1 ; c1.signal(); } finally { lock.unlock(); } } }public class SyncAndReentrantLockDemo { public static void main (String[] args) { ShareResource shareResource = new ShareResource (); new Thread (() -> { for (int i = 1 ; i <= 10 ; i++) { shareResource.print5(); } }, "A" ).start(); new Thread (() -> { for (int i = 1 ; i <= 10 ; i++) { shareResource.print10(); } }, "B" ).start(); new Thread (() -> { for (int i = 1 ; i <= 10 ; i++) { shareResource.print15(); } }, "C" ).start(); } }
线程通信之生产者消费者阻塞队列版 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 package com.atguigu.thread;import java.util.concurrent.ArrayBlockingQueue;import java.util.concurrent.BlockingQueue;import java.util.concurrent.TimeUnit;import java.util.concurrent.atomic.AtomicInteger;class MyResource { private volatile boolean FLAG = true ; private AtomicInteger atomicInteger = new AtomicInteger (); BlockingQueue<String> blockingQueue = null ; public MyResource (BlockingQueue<String> blockingQueue) { this .blockingQueue = blockingQueue; System.out.println(blockingQueue.getClass().getName()); } public void myProd () throws Exception { String data = null ; boolean retValue; while (FLAG) { data = atomicInteger.incrementAndGet() + "" ; retValue = blockingQueue.offer(data, 2L , TimeUnit.SECONDS); if (retValue) { System.out.println(Thread.currentThread().getName() + "\t插入队列" + data + "成功" ); } else { System.out.println(Thread.currentThread().getName() + "\t插入队列" + data + "失败" ); } TimeUnit.SECONDS.sleep(1 ); } System.out.println(Thread.currentThread().getName() + "\t大老板叫停了, 表示FLAG = false, 生产动作结束" ); } public void myConsumer () throws Exception { String result = null ; while (FLAG) { result = blockingQueue.poll(2L , TimeUnit.SECONDS); if (null == result || result.equalsIgnoreCase("" )) { FLAG = false ; System.out.println(Thread.currentThread().getName() + "\t超过2秒钟没有取到蛋糕, 消费退出" ); System.out.println(); System.out.println(); return ; } System.out.println(Thread.currentThread().getName() + "\t消费队列蛋糕" + result + "成功" ); } } public void stop () { this .FLAG = false ; } }public class ProdConsumer_BlockQueueDemo { public static void main (String[] args) { MyResource myResource = new MyResource (new LinkedBlockingQueue <>(10 )); System.out.println(Thread.currentThread().getName() + "\t生产线程启动" ); try { myResource.myProd(); } catch (Exception e) { throw new RuntimeException (e); } }, "Prod" ).start(); new Thread (() -> { System.out.println(Thread.currentThread().getName() + "\t消费线程启动" ); System.out.println(); System.out.println(); try { myResource.myConsumer(); } catch (Exception e) { throw new RuntimeException (e); } }, "Consumer" ).start(); try { TimeUnit.SECONDS.sleep(5 ); } catch (InterruptedException e) { throw new RuntimeException (e); } System.out.println(); System.out.println(); System.out.println(); System.out.println("5秒钟时间到, 大老板main线程叫停, 活动结束" ); myResource.stop(); } }
线程池 线程池使用及优势 为什么用线程池,优势
线程池做的工作主要是控制运行的线程的数量,处理过程中将任务放入队列,然后在线程创建后启动这些任务,如果线程数量超过了最大数量超出数量的线程排队等候,等其它线程执行完毕,再从队列中取出任务来执行。
他的主要特点为:线程复用,控制最大并发数;管理线程
第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。 第二:提高响应速度。当任务到达时,任务可以不需要的等到线程创建就能立即执行。 第三:提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控
1 2 3 4 5 6 7 8 9 package com.atguigu.thread;public class MyThreadPoolDemo { public static void main (String[] args) { System.out.println(Runtime.getRuntime().availableProcessors()); } }
Java中线程池是通过Executor框架实现的, 该框架中用到了 Executor , Executors, ExecutorService, ThreadPoolExecutor这几个类
常用的几个创建线程池的方法
1 2 3 4 5 6 7 Executors.newFixedThreadPool(int parallelism); Executors.newSingleThreadExecutor(); Executors.newCachedThreadPool();
线程池3个常用方式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 package com.atguigu.thread;import java.util.concurrent.Executor;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.TimeUnit;public class MyThreadPoolDemo { public static void main (String[] args) { ExecutorService threadPool = Executors.newCachedThreadPool(); try { for (int i = 1 ; i <= 10 ; i++) { threadPool.execute(() -> { System.out.println(Thread.currentThread().getName() + "\t 办理业务" ); }); } } finally { threadPool.shutdown(); } } }
Executors.newFixedThreadPool(5); 执行长期任务, 性能好很多 Executors.newSingleThreadExecutor(); 一个任务一个任务执行的场景 Executors.newCachedThreadPool(); 适用: 执行很多短期异步的小程序或者负载较轻的服务器
以上三个方法的底层实现其实都是通过返回 ThreadPoolExecutor
对象实现的
创建线程的有哪几种方式? 创建线程的有哪几种方式? (1) 继承Thread类 (2) 实现Runable接口, 没有返回值, 不抛异常, 实现run()方法 (3) 实现Callable接口, 有返回值, 抛异常, 实现call()方法 , 支持泛型 (4) Executor 线程池
线程池的几个重要参数介绍? 通过观察源码发现, 最终返回的都是 ThreadPoolExecutor()
方法 Ctrl B查看源码
1 2 3 4 5 6 7 8 public ThreadPoolExecutor (int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) { this (corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors.defaultThreadFactory(), defaultHandler); }
发现 ThreadPoolExecutor 接收参数之后调用了 this, this构造方法如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 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; }
总共七个参数, 如下
说说线程池的底层工作原理?
线程池的4种拒绝策略你谈谈 是什么? 等待队列也已经排满了,再也塞不下新任务了司时, 线程池中的max线程也达到了,无法继续为新任务服务 这时候我们就需要拒绝策略机制合理的处理这个问题。
AbortPolicy(默认): 直接抛出 RejectedExecutionException 异常阻止系统正常运行 CallerRunsPolicy: “调用者运行”一种调节机制,该策略既不会抛弃任务,也不会抛出异常,而是将某些任务回退到调用者, 从而降低新任务的流量 DiscardoldestPolicy: 抛弃队列中等待最久的任务,然后把当前任务加入队列中尝试再次提交当前任务 DiscardPolicy: 直接丢弃任务,不予任何处理也不抛出异常。如果允许任务丢失,这是最好的一种方案
以上内置拒绝策略均实现了RejectedExecutionHandler接口
你在工作中单一的/固定数的/可变的三种创建线程池的方法, 你用的哪个多? 超级大坑 你在工作中单一的/固定数的/可变的三种创建线程池的方法, 你用的哪个多? 超级大坑 答案是一个都不用, 我们生产上只能使用自定义的 Executors中JDK已经给你提供好了, 为什么不用?
通过观察底层源码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 public static ExecutorService newFixedThreadPool (int nThreads, ThreadFactory threadFactory) { return new ThreadPoolExecutor (nThreads, nThreads, 0L , TimeUnit.MILLISECONDS, new LinkedBlockingQueue <Runnable>(), threadFactory); }public static ExecutorService newSingleThreadExecutor () { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor (1 , 1 , 0L , TimeUnit.MILLISECONDS, new LinkedBlockingQueue <Runnable>())); }public LinkedBlockingQueue () { this (Integer.MAX_VALUE); }public static ExecutorService newCachedThreadPool () { return new ThreadPoolExecutor (0 , Integer.MAX_VALUE, 60L , TimeUnit.SECONDS, new SynchronousQueue <Runnable>()); }
使用 Executors 创建出来的线程池, 都是无界的队列, 这可能会非常消耗资源
并且, 在阿里巴巴手册提及到
【强制】线程资源必须通过线程池提供,不允许在应用中自行显式创建线程。 说明:线程池的好处是减少在创建和销毁线程上所消耗的时间以及系统资源的开销,解决资源不足的问题。 如果不使用线程池,有可能造成系统创建大量同类线程而导致消耗完内存或者“过度切换”的问题。
【强制】线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这 样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。 说明:Executors 返回的线程池对象的弊端如下: 1) FixedThreadPool 和 SingleThreadPool: 允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。 2) CachedThreadPool: 允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。
你在工作中是如何使用线程池的, 是否自定义过线程池使用? 使用 new ThreadPoolExecutor()
自定义线程池 触发拒绝策略的情况: 任务数量 > maximumPoolSize + capacity
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 package com.atguigu.thread;import java.util.concurrent.*;public class MyThreadPoolDemo { public static void main (String[] args) { ExecutorService threadPool = new ThreadPoolExecutor ( 3 , 5 , 1L , TimeUnit.SECONDS, new LinkedBlockingQueue <Runnable>(3 ), Executors.defaultThreadFactory(), new ThreadPoolExecutor .DiscardPolicy() ); try { for (int i = 1 ; i <= 10 ; i++) { threadPool.execute(() -> { System.out.println(Thread.currentThread().getName() + "\t 办理业务" ); }); } } finally { threadPool.shutdown(); } } private static void threadPoolInit () { ExecutorService threadPool = Executors.newCachedThreadPool(); try { for (int i = 1 ; i <= 10 ; i++) { threadPool.execute(() -> { System.out.println(Thread.currentThread().getName() + "\t 办理业务" ); }); } } finally { threadPool.shutdown(); } } }
合理配置线程池你是如何考虑的? CPU密集型: IO密集型: 1. 2.
死锁编码及定位分析 什么是死锁?
死锁产生的主要原因: 1.系统资源不足 2.进程运行推进的顺序不合适 3.资源分配不当
死锁demo
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 package com.atguigu.thread;import java.util.concurrent.TimeUnit;class HoldLockThread implements Runnable { private String lockA; private String lockB; public HoldLockThread (String lockA, String lockB) { this .lockA = lockA; this .lockB = lockB; } @Override public void run () { synchronized (lockA) { System.out.println(Thread.currentThread().getName() + "\t 自己持有: " + lockA + "\t 尝试获得: " + lockB); try { TimeUnit.SECONDS.sleep(2 ); } catch (InterruptedException e) { throw new RuntimeException (e); } synchronized (lockB) { System.out.println(Thread.currentThread().getName() + "\t 自己持有: " + lockB + "\t 尝试获得: " + lockA); } } } }public class DeadLockDemo { public static void main (String[] args) { String lockA = "lockA" ; String lockB = "lockB" ; new Thread (new HoldLockThread (lockA, lockB), "AAA" ).start(); new Thread (new HoldLockThread (lockB, lockA), "BBB" ).start(); } }
如何排查死锁? jps命令定位进程号 jstack找到死锁查看
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 $ jps -l 14352 org.jetbrains.jps.cmdline.Launcher 34612 35716 com.atguigu.thread.DeadLockDemo 14408 org.codehaus.classworlds.Launcher 12892 sun.tools.jps.Jps $ jstack 35716 2024-01-10 01:36:35 Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.141-b15 mixed mode): "DestroyJavaVM" java.lang.Thread.State: RUNNABLE "BBB" java.lang.Thread.State: BLOCKED (on object monitor) at com.atguigu.thread.HoldLockThread.run(DeadLockDemo.java:27) - waiting to lock <0x000000076bba2fa8> (a java.lang.String) - locked <0x000000076bba2fe0> (a java.lang.String) at java.lang.Thread.run(Thread.java:748) "AAA" java.lang.Thread.State: BLOCKED (on object monitor) at com.atguigu.thread.HoldLockThread.run(DeadLockDemo.java:27) - waiting to lock <0x000000076bba2fe0> (a java.lang.String) - locked <0x000000076bba2fa8> (a java.lang.String) at java.lang.Thread.run(Thread.java:748) "Service Thread" java.lang.Thread.State: RUNNABLE "C1 CompilerThread3" java.lang.Thread.State: RUNNABLE "C2 CompilerThread2" java.lang.Thread.State: RUNNABLE"C2 CompilerThread1" java.lang.Thread.State: RUNNABLE"C2 CompilerThread0" java.lang.Thread.State: RUNNABLE"Monitor Ctrl-Break" java.lang.Thread.State: RUNNABLE at java.net.SocketInputStream.socketRead0(Native Method) at java.net.SocketInputStream.socketRead(SocketInputStream.java:116) at java.net.SocketInputStream.read (SocketInputStream.java:171) at java.net.SocketInputStream.read (SocketInputStream.java:141) at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284) at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326) at sun.nio.cs.StreamDecoder.read (StreamDecoder.java:178) - locked <0x000000076ba85008> (a java.io.InputStreamReader) at java.io.InputStreamReader.read (InputStreamReader.java:184) at java.io.BufferedReader.fill(BufferedReader.java:161) at java.io.BufferedReader.readLine(BufferedReader.java:324) - locked <0x000000076ba85008> (a java.io.InputStreamReader) at java.io.BufferedReader.readLine(BufferedReader.java:389) at com.intellij.rt.execution.application.AppMainV2$1 .run(AppMainV2.java:53)"Attach Listener" java.lang.Thread.State: RUNNABLE"Signal Dispatcher" java.lang.Thread.State: RUNNABLE"Finalizer" java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait (Native Method) - waiting on <0x000000076b908ec8> (a java.lang.ref.ReferenceQueue$Lock ) at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:143) - locked <0x000000076b908ec8> (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" java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait (Native Method) - waiting on <0x000000076b906b68> (a java.lang.ref.Reference$Lock ) at java.lang.Object.wait (Object.java:502) at java.lang.ref.Reference.tryHandlePending(Reference.java:191) - locked <0x000000076b906b68> (a java.lang.ref.Reference$Lock ) at java.lang.ref.Reference$ReferenceHandler .run(Reference.java:153)"VM Thread" os_prio=2 tid=0x000000001c3a9800 nid=0x1694 runnable"GC task thread#0 (ParallelGC)" os_prio=0 tid=0x0000000002938000 nid=0x6590 runnable"GC task thread#1 (ParallelGC)" os_prio=0 tid=0x0000000002939800 nid=0x24dc runnable"GC task thread#2 (ParallelGC)" os_prio=0 tid=0x000000000293b000 nid=0x5930 runnable"GC task thread#3 (ParallelGC)" os_prio=0 tid=0x000000000293c800 nid=0x5d98 runnable"GC task thread#4 (ParallelGC)" os_prio=0 tid=0x000000000293f000 nid=0x4390 runnable"GC task thread#5 (ParallelGC)" os_prio=0 tid=0x0000000002941000 nid=0x7950 runnable Found one Java-level deadlock: ============================="BBB" : waiting to lock monitor 0x000000001c3b0d98 (object 0x000000076bba2fa8, a java.lang.String), which is held by "AAA" "AAA" : waiting to lock monitor 0x000000001c3b3158 (object 0x000000076bba2fe0, a java.lang.String), which is held by "BBB" Java stack information for the threads listed above: ==================================================="BBB" : at com.atguigu.thread.HoldLockThread.run(DeadLockDemo.java:27) - waiting to lock <0x000000076bba2fa8> (a java.lang.String) - locked <0x000000076bba2fe0> (a java.lang.String) at java.lang.Thread.run(Thread.java:748)"AAA" : at com.atguigu.thread.HoldLockThread.run(DeadLockDemo.java:27) - waiting to lock <0x000000076bba2fe0> (a java.lang.String) - locked <0x000000076bba2fa8> (a java.lang.String) at java.lang.Thread.run(Thread.java:748) Found 1 deadlock.
常见的垃圾回收算法 0.JVM体系结构概述
1.引用计数
2.复制
3.标记清除
4.标记整理
JVM复习题 1.JVM垃圾回收的时候如何确定垃圾?是否知道什么是GC Roots 1.什么是垃圾? 简单的说就是内存中已经不再被使用到的空间就是垃圾
2.要进行垃圾回收, 如何判断一个对象是否可以被回收? (1)引用计数法 (2)枚举根节点做可达性分析(根搜索路径)
a)case
b)Java中可以作为CG Roots的对象 1) 虚拟机栈(栈帧中的局部变量区,也叫做局部变量表) 中引用的对象 2) 方法区中的类静态属性引用的对象。 3) 方法区中常量引用的对象。 4) 本地方法栈中JNI(Native方法)引用的对象
Person p1 = new Person(); 左边: 引用 右边: 实例对象
2.你说你做过JVM调优和参数配置,请问如何盘点查看JVM系统默认值
JVM的参数类型 (1) 标配参数 (1.1) -version (1.2) -help (1.3) java -showversion
(2) X参数(了解) (2.1) -Xint 解释执行 (2.2) -Xcomp 第一次使用就编译成本地代码 (2.3) -Xmixed 混合模式 (先编译, 再执行)
(3) XX参数 (3.1) Boolean类型 a) 公式 -XX: +
或者 -
某个属性值 +表示开启 -表示关闭 b) case: c) 是否打印GC收集细节 c1) -XX: -PrintGCDetails c2) -XX: +PrintGCDetails d) 是否使用串行垃圾回收器 d1) -XX: -UseSerialGC d2) -XX: +UseSerialGC
如何查看一个正在运行中的Java程序, 它的某个jvm参数是否开启? 具体指是多少? jps jinfo
编写如下代码:
1 2 3 4 5 6 7 8 9 package com.atguigu.thread;public class HelloGC { public static void main (String[] args) throws InterruptedException { System.out.println("HelloGC" ); Thread.sleep(Integer.MAX_VALUE); } }
输入如下命令进行查看正在运行的java程序是否开启了某个jvm参数
1 2 $ jps -l $ jinfo -flag PrintGCDetails 37860
在VM Options中添加参数 -XX:+PrintGCDetails
, 再次查看此时运行的jvm参数, 变成了 +
, 之前默认是 -
(3.2) KV设值类型 a) 公式 -XX:属性key=属性值value b) Case c) -XX:MetaspaceSize=128m d) -XX:MaxTenuringThreshold=15
1 2 3 $ jps -l $ jinfo -flag MetaspaceSize 29280 $ jinfo -flag MaxTenuringThreshold 29280
查看默认的 元空间大小 21807104 / 1024 / 1024 ≈ 21M
修改元空间大小, 设置VM options : -XX:MetaspaceSize=128m
同理可以查看 MaxTenuringThreshold 设置对象进入老年代的最大年龄阈值
(4) jinfo举例, 如何查看当前运行程序的配置 公式: jinfo -flag 配置项进程编号
1 2 3 jps jinfo -flag 具体参数 java进程编号 jinfo -flags java进程编号
(5) 题外话(坑题) 两个经典参数: -Xms和-Xmx 这个你如何解释? -Xms 等价于 -XX:InitialHeapSize -Xmx 等价于 -XX:MaxHeapSize
判断家底查看JVM默认值 (1) -XX:+PrintFlagsInitial 主要查看初始默认 公式 java -XX:+PrintFlagsInitial -version
java -XX:+PrintFlagsInitial
(2) -XX:+PrintFlagsFinal 主要查看修改更新 公式 java -XX:+PrintFlagsFinal -version
公式 java -XX:+PrintFlagsFinal -version
:= 说明被人为的修改过了, =表示是默认值
(3) PrintFlagsFinal 举例, 运行java命令的同时打印参数
1 2 3 4 5 6 7 8 9 10 11 package com.atguigu.thread;public class T { public static void main (String[] args) { int a = 100 ; int b = 200 ; int retValue = a + b; System.out.println("retValue = " + retValue); } }
编译运行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 PS E:\Code\面试题\大厂面试题第2季\src\main\java\com\atguigu\thread> pwd Path ---- E:\Code\面试题\大厂面试题第2季\src\main\java\com\atguigu\thread PS E:\Code\面试题\大厂面试题第2季\src\main\java\com\atguigu\thread> more .\T.java package com.atguigu.thread; public class T { public static void main(String[] args) { int a = 100; int b = 200; int retValue = a + b; System.out.println("retValue = " + retValue); } } PS E:\Code\面试题\大厂面试题第2季\src\main\java\com\atguigu\thread> javac T.java PS E:\Code\面试题\大厂面试题第2季\src\main\java\com\atguigu\thread> ls -a---- 2024/1/11 15:07 652 T.class -a---- 2024/1/11 14:47 235 T.java PS E:\Code\面试题\大厂面试题第2季\src\main\java\com\atguigu\thread> cd ../../../ PS E:\Code\面试题\大厂面试题第2季\src\main\java> java com.atguigu.thread.T retValue = 300 PS E:\Code\面试题\大厂面试题第2季\src\main\java> java -cp . com.atguigu.thread.T retValue = 300 参考: https://www.liaoxuefeng.com/wiki/1252599548343744/1260466914339296
运行java命令的同时打印参数, 可以观察到 , 可以通过自己手动指定参数将参数的默认值进行修改
1 2 3 4 5 6 7 8 9 PS E:\Code\面试题\大厂面试题第2季\src\main\java> java -XX:+PrintFlagsFinal com.atguigu.thread.T uintx MetaspaceSize = 21807104 {pd product} retValue = 300 PS E:\Code\面试题\大厂面试题第2季\src\main\java> java -XX:+PrintFlagsFinal -XX:MetaspaceSize=512m com.atguigu.thread.T uintx MetaspaceSize := 536870912 {pd product} retValue = 300
(4) -XX:+PrintCommandLineFlags 打印命令行参数
1 2 3 4 5 6 $ java -XX:+PrintCommandLineFlags -version -XX:InitialHeapSize=265625408 -XX:MaxHeapSize=4250006528 -XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:+UseParallelGC java version "1.8.0_141" Java(TM) SE Runtime Environment (build 1.8.0_141-b15) Java HotSpot(TM) 64-Bit Server VM (build 25.141-b15, mixed mode)
3.你平时工作用过的JVM常用基本配置参数有哪些? 1.基础知识复习
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 package com.atguigu.thread;public class HelloGC { public static void main (String[] args) throws InterruptedException { long totalMemory = Runtime.getRuntime().totalMemory(); long maxMemory = Runtime.getRuntime().maxMemory(); System.out.println("TOTAL_MEMORY(-Xms) = " + totalMemory + "(字节)、" + (totalMemory / (double ) 1024 / 1024 ) + "MB" ); System.out.println("MAX_MEMORY(-Xmx) = " + maxMemory + "(字节)、" + (maxMemory / (double ) 1024 / 1024 ) + "MB" ); } }
2.常用参数 -Xms: 初始大小内存, 默认为物理内存1/64 等价于-XX:InitialHeapSize
-Xmx 初始大小内存, 默认为物理内存1/4 等价于-XX:MaxHeapSize
-Xss 设置单个线程栈的大小, 一般默认为512k~1024k 等价于-XX:ThreadStackSize
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 $ cat .\HelloGC.java package com.atguigu.thread; public class HelloGC { public static void main(String[] args) throws InterruptedException { System.out.println("HelloGC" ); Thread.sleep (Integer.MAX_VALUE); } } $ javac -encoding UTF-8 .\HelloGC.java $ cd ../../.. $ java com.atguigu.thread.HelloGC HelloGC $ jps -l 1376 org.jetbrains.jps.cmdline.Launcher 34612 9444 com.atguigu.thread.HelloGC 14408 org.codehaus.classworlds.Launcher 33576 sun.tools.jps.Jps $ jinfo -flag ThreadStackSize 9444 -XX:ThreadStackSize=0 $ java -Xss128k com.atguigu.thread.HelloGC HelloGC $ jps -l 1376 org.jetbrains.jps.cmdline.Launcher 34612 14408 org.codehaus.classworlds.Launcher 24744 sun.tools.jps.Jps 28616 com.atguigu.thread.HelloGC $ jinfo -flag ThreadStackSize 28616 -XX:ThreadStackSize=128
-XX:ThreadStackSize=0
并不是说明栈空间大小是0, 而是表明使用平台默认值
-Xmn -Xmn: 设置年轻代大小
设置元空间大小: 元空间的本质和永久代类似,都是对JVM规范中方法区的实现。 不过元空间与永久代之间最大的区别在于: 元空间并不在虚拟机中,而是使用本地内存。因此,默认情况下,元空间的大小仅受本地内存限制 修改元空间大小:
1 -Xms10m -Xmx10m -XX:MetaspaceSize=1024m -XX:PrintFlagsFinal
添加参数 -XX:+PrintFlagsFinal
查看
1 uintx MetaspaceSize = 21807104 {pd product}
jdk中默认的元空间的大小是 21807104 换算 21807104 / 1024 / 1024 = 20.796875 ≈ 21 M
典型设置案例 添加参数查看 -XX:+PrintCommandLineFlags
1 2 -XX:InitialHeapSize=265625408 -XX:MaxHeapSize=4250006528 -XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:+UseParallelGC
修改参数
1 -Xms128m -Xmx4096m -Xss1024k -XX:MetaspaceSize=512m -XX:+PrintCommandLineFlags -XX:+PrintGCDetails -XX:+UseSerialGC
运行得到
1 -XX:InitialHeapSize=134217728 -XX:MaxHeapSize=4294967296 -XX:MetaspaceSize=536870912 -XX:+PrintCommandLineFlags -XX:+PrintGCDetails -XX:ThreadStackSize=1024 -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:+UseSerialGC
-XX:+PrintGCDetails 输出详细GC收集日志信息: -XX:+PrintGCDetails
1 2 3 4 5 6 7 8 9 package com.atguigu.thread;public class HelloGC { public static void main (String[] args) throws InterruptedException { System.out.println("HelloGC" ); byte [] bytes = new byte [50 * 1024 * 1024 ]; } }
vm Options中添加参数
运行结果
1 2 3 4 5 6 7 8 9 10 HelloGC Heap PSYoungGen total 75776K, used 57708K [0x000000076b900000, 0x0000000770d80000, 0x00000007c0000000) eden space 65024K, 88% used [0x000000076b900000,0x000000076f15b368,0x000000076f880000) from space 10752K, 0% used [0x0000000770300000,0x0000000770300000,0x0000000770d80000) to space 10752K, 0% used [0x000000076f880000,0x000000076f880000,0x0000000770300000) ParOldGen total 173568K, used 0K [0x00000006c2a00000, 0x00000006cd380000, 0x000000076b900000) object space 173568K, 0% used [0x00000006c2a00000,0x00000006c2a00000,0x00000006cd380000) Metaspace used 3277K, capacity 4496K, committed 4864K, reserved 1056768K class space used 359K, capacity 388K, committed 512K, reserved 1048576K
修改参数
1 -Xms10m -Xmx10m -XX:+PrintGCDetails
运行结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 HelloGC [GC (Allocation Failure) [PSYoungGen: 1746K->488K(2560K)] 1746K->696K(9728K), 0.0009593 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [GC (Allocation Failure) [PSYoungGen: 488K->488K(2560K)] 696K->712K(9728K), 0.0005381 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [Full GC (Allocation Failure) [PSYoungGen: 488K->0K(2560K)] [ParOldGen: 224K->614K(7168K)] 712K->614K(9728K), [Metaspace: 3257K->3257K(1056768K)], 0.0069167 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] [GC (Allocation Failure) [PSYoungGen: 0K->0K(2560K)] 614K->614K(9728K), 0.0003632 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [Full GC (Allocation Failure) [PSYoungGen: 0K->0K(2560K)] [ParOldGen: 614K->597K(7168K)] 614K->597K(9728K), [Metaspace: 3257K->3257K(1056768K)], 0.0075417 secs] [Times: user=0.08 sys=0.00, real=0.01 secs] Heap PSYoungGen total 2560K, used 96K [0x00000000ffd00000, 0x0000000100000000, 0x0000000100000000) eden space 2048K, 4% used [0x00000000ffd00000,0x00000000ffd18330,0x00000000fff00000) from space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000) to space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000) ParOldGen total 7168K, used 597K [0x00000000ff600000, 0x00000000ffd00000, 0x00000000ffd00000) object space 7168K, 8% used [0x00000000ff600000,0x00000000ff6957c0,0x00000000ffd00000) Metaspace used 3302K, capacity 4496K, committed 4864K, reserved 1056768K class space used 362K, capacity 388K, committed 512K, reserved 1048576K Exception in thread "main" java.lang.OutOfMemoryError: Java heap space at com.atguigu.thread.HelloGC.main(HelloGC.java:6)
GC
FullGC
-XX:SurvivorRatio
设置新生代中eden和S0/S1空间的比例 默认 -XX:SurvivorRatio=8
Eden:S0:S1 = 8:1:1 假如 -XX:SurvivorRatio=4
Eden:S0:S1 = 4:1:1 SurvivorRatio值就是设置eden区的比例占多少, S0/S1相同
代码
1 2 3 4 5 6 7 8 package com.atguigu.thread;public class HelloGC { public static void main (String[] args) throws InterruptedException { System.out.println("HelloGC" ); } }
添加参数
1 -XX:+PrintGCDetails -XX:+UseSerialGC -Xms10m -Xmx10m
或者添加参数, 只是省略了 SurvivorRatio
的配置, 默认 SurvivorRatio
就是8
1 2 -XX:+PrintGCDetails -XX:+UseSerialGC -Xms10m -Xmx10m -XX:SurvivorRatio=8
运行结果: eden:from:to = 2752K:320K:320K ≈ 8:1:1
1 2 3 4 5 6 7 8 9 10 HelloGC Heap def new generation total 3072K, used 1774K [0x00000000ff600000, 0x00000000ff950000, 0x00000000ff950000) eden space 2752K, 64% used [0x00000000ff600000, 0x00000000ff7bbac0, 0x00000000ff8b0000) from space 320K, 0% used [0x00000000ff8b0000, 0x00000000ff8b0000, 0x00000000ff900000) to space 320K, 0% used [0x00000000ff900000, 0x00000000ff900000, 0x00000000ff950000) tenured generation total 6848K, used 0K [0x00000000ff950000, 0x0000000100000000, 0x0000000100000000) the space 6848K, 0% used [0x00000000ff950000, 0x00000000ff950000, 0x00000000ff950200, 0x0000000100000000) Metaspace used 3237K, capacity 4496K, committed 4864K, reserved 1056768K class space used 353K, capacity 388K, committed 512K, reserved 1048576K
修改参数
1 -XX:+PrintGCDetails -XX:+UseSerialGC -Xms10m -Xmx10m -XX:SurvivorRatio=4
运行结果: eden:from:to = 2368K:512K:512K ≈ 4:1:1
1 2 3 4 5 6 7 8 9 10 HelloGC Heap def new generation total 2880K, used 1754K [0x00000000ff600000, 0x00000000ff950000, 0x00000000ff950000) eden space 2368K, 74% used [0x00000000ff600000, 0x00000000ff7b6b48, 0x00000000ff850000) from space 512K, 0% used [0x00000000ff850000, 0x00000000ff850000, 0x00000000ff8d0000) to space 512K, 0% used [0x00000000ff8d0000, 0x00000000ff8d0000, 0x00000000ff950000) tenured generation total 6848K, used 0K [0x00000000ff950000, 0x0000000100000000, 0x0000000100000000) the space 6848K, 0% used [0x00000000ff950000, 0x00000000ff950000, 0x00000000ff950200, 0x0000000100000000) Metaspace used 3235K, capacity 4496K, committed 4864K, reserved 1056768K class space used 353K, capacity 388K, committed 512K, reserved 1048576K
-XX:NewRatio 配置年轻代与老年代在堆结构的占比 默认 -XX:NewRatio=2新生代占1, 老年代2, 年轻代占整个堆的1/3 假如 -XX:NewRatio=4新生代占1, 老年代4, 年轻代占整个堆的1/5 NewRatio值就是设置老年代的占比,剩下的1给新生代
参数:
1 2 3 4 -Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+UseSerialGC -XX:NewRatio=2 -Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+UseSerialGC -XX:NewRatio=4
运行结果: 修改前: 新生代: 老年代 = 3072K : 6848K ≈ 1 : 2 修改后: 新生代: 老年代 = 1856K : 8192K ≈ 1 : 4
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 HelloGC Heap def new generation total 3072K, used 1778K [0x00000000ff600000, 0x00000000ff950000, 0x00000000ff950000) eden space 2752K, 64% used [0x00000000ff600000, 0x00000000ff7bca40, 0x00000000ff8b0000) from space 320K, 0% used [0x00000000ff8b0000, 0x00000000ff8b0000, 0x00000000ff900000) to space 320K, 0% used [0x00000000ff900000, 0x00000000ff900000, 0x00000000ff950000) tenured generation total 6848K, used 0K [0x00000000ff950000, 0x0000000100000000, 0x0000000100000000) the space 6848K, 0% used [0x00000000ff950000, 0x00000000ff950000, 0x00000000ff950200, 0x0000000100000000) Metaspace used 3242K, capacity 4496K, committed 4864K, reserved 1056768K class space used 354K, capacity 388K, committed 512K, reserved 1048576K HelloGC [GC (Allocation Failure) [DefNew: 1664K->191K(1856K), 0.0018514 secs] 1664K->611K(10048K), 0.0018916 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] Heap def new generation total 1856K, used 231K [0x00000000ff600000, 0x00000000ff800000, 0x00000000ff800000) eden space 1664K, 2% used [0x00000000ff600000, 0x00000000ff609fe0, 0x00000000ff7a0000) from space 192K, 99% used [0x00000000ff7d0000, 0x00000000ff7ffff8, 0x00000000ff800000) to space 192K, 0% used [0x00000000ff7a0000, 0x00000000ff7a0000, 0x00000000ff7d0000) tenured generation total 8192K, used 419K [0x00000000ff800000, 0x0000000100000000, 0x0000000100000000) the space 8192K, 5% used [0x00000000ff800000, 0x00000000ff868d80, 0x00000000ff868e00, 0x0000000100000000) Metaspace used 3215K, capacity 4496K, committed 4864K, reserved 1056768K class space used 352K, capacity 388K, committed 512K, reserved 1048576K
-XX:MaxTenuringThreshold 设置垃圾最大年龄
1 2 3 4 5 6 7 8 package com.atguigu.thread;public class HelloGC { public static void main (String[] args) throws InterruptedException { System.out.println("HelloGC" ); Thread.sleep(Integer.MAX_VALUE); } }
老年代年龄默认是15
-XX:MaxTenuringThreshold=0: 设置垃圾最大年龄。如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代。对于年老代比较多的应用,可以提高效率。如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象再年轻代的存活时间,增加在年轻代即被回收的概论。
添加参数
1 -XX:MaxTenuringThreshold=12
值得注意的是 MaxTenuringThreshold
的取值范围是: 0~15
1 2 3 Error: Could not create the Java Virtual Machine. Error: A fatal exception has occurred. Program will exit . MaxTenuringThreshold of 20 is invalid; must be between 0 and 15
运行结果:
1 2 3 4 5 6 7 8 9 10 11 $ jps -l 27156 org.jetbrains.jps.cmdline.Launcher 34612 14408 org.codehaus.classworlds.Launcher 7784 sun.tools.jps.Jps 36412 com.atguigu.thread.HelloGC $ jinfo -flag MaxTenuringThreshold 36412 -XX:MaxTenuringThreshold=12 $ jinfo -flag PrintGCDetails 36412 -XX:-PrintGCDetails
4.强引用、软引用、弱引用、虚引用分别是什么? 整体架构:
强引用(默认支持模式)
obj2 强引用, 不会被垃圾回收
1 2 3 4 5 6 7 8 9 10 11 12 package com.atguigu.jvm;public class StrongReferenceDemo { public static void main (String[] args) { Object obj1 = new Object (); Object obj2 = obj1; obj1 = null ; System.gc(); System.out.println(obj2); } }
软引用 SoftReference 当系统内存充足时它不会被回收 当系统内存不足时它会被回收
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 package com.atguigu.jvm;import java.lang.ref.SoftReference;public class SoftReferenceDemo { public static void softRef_Memory_Enough () { Object o1 = new Object (); SoftReference<Object> softReference = new SoftReference <>(o1); System.out.println(o1); System.out.println(softReference.get()); o1 = null ; System.gc(); System.out.println(o1); System.out.println(softReference.get()); } public static void softRef_Memory_NotEnough () { Object o1 = new Object (); SoftReference<Object> softReference = new SoftReference <>(o1); System.out.println(o1); System.out.println(softReference.get()); o1 = null ; try { byte [] bytes = new byte [30 * 1024 * 1024 ]; } catch (Exception e) { throw new RuntimeException (e); } finally { System.out.println(o1); System.out.println(softReference.get()); } } public static void main (String[] args) { softRef_Memory_NotEnough(); } }
弱引用 WeakReference case 不管内存够不够用, 最终都会回收该对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package com.atguigu.jvm;import java.lang.ref.WeakReference;public class WeakReferenceDemo { public static void main (String[] args) { Object o1 = new Object (); WeakReference<Object> weakReference = new WeakReference <>(o1); System.out.println(o1); System.out.println(weakReference.get()); o1 = null ; System.gc(); System.out.println(o1); System.out.println(weakReference.get()); } }
软引用和弱引用的适用场景
你知道弱引用的话, 能谈谈WeakHashMap吗?
WeakHashMap继承AbstractMap,实现了Map接口。和HashMap一样,WeakHashMap也是一个散列表,它存储的内容也是键值对(key-value)映射,而且键和值都可以是null。 不过WeakHashMap的键是”弱键”。在WeakHashMap中,当某个键不再正常使用时,会被从WeakHashMap中被自动移除 。更精确地说,对于一个给定的键,其映射的存在并不阻止垃圾回收器对该键的丢弃,这就使该键成为可终止的,被终止,然后被回收。某个键被终止时,它对应的键值对也就从映射中有效地移除了。 WeakHashMap内部是通过弱引用来管理entry的,弱引用的特性对应到WeakHashMap上意味着什么呢?将一对key, value放入到WeakHashMap里并不能避免该key值被GC回收,除非在WeakHashMap之外还有对该key的强引用。 和HashMap一样,WeakHashMap是不同步的。可以使用Collections.synchronizedMap方法来构造同步的WeakHashMap。 参考: https://blog.csdn.net/u014294681/article/details/86522487
在WeakHashMap中,当某个键不再正常使用时,会被从WeakHashMap中被自动移除
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 package com.atguigu.jvm;import java.util.HashMap;import java.util.WeakHashMap;public class WeakHashDemo { public static void main (String[] args) { myHashMap(); System.out.println(); myWeakHashMap(); } private static void myHashMap () { HashMap<Integer, String> map = new HashMap <>(); Integer key = new Integer (1 ); String value = "HashMap" ; map.put(key, value); System.out.println(map); key = null ; System.out.println(map); System.gc(); System.out.println(map); } private static void myWeakHashMap () { WeakHashMap<Integer, String> map = new WeakHashMap <>(); Integer key = new Integer (2 ); String value = "WeakHashMap" ; map.put(key, value); System.out.println(map); key = null ; System.out.println(map); System.gc(); System.out.println(map); } }
虚引用 PhantomReference 虚引用
引用队列
引用队列case 虚引用在垃圾回收之后, 会被放到引用队列中进行保存
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 package com.atguigu.jvm;import java.lang.ref.ReferenceQueue;import java.lang.ref.WeakReference;public class ReferenceQueueDemo { public static void main (String[] args) throws InterruptedException { Object o1 = new Object (); ReferenceQueue<Object> referenceQueue = new ReferenceQueue <>(); WeakReference<Object> weakReference = new WeakReference <>(o1, referenceQueue); System.out.println(o1); System.out.println(weakReference.get()); System.out.println(referenceQueue.poll()); System.out.println(); o1 = null ; System.gc(); Thread.sleep(500 ); System.out.println(o1); System.out.println(weakReference.get()); System.out.println(referenceQueue.poll()); } }
虚引用case 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 package com.atguigu.jvm;import java.lang.ref.PhantomReference;import java.lang.ref.ReferenceQueue;public class PhantomReferenceDemo { public static void main (String[] args) { Object o1 = new Object (); ReferenceQueue<Object> referenceQueue = new ReferenceQueue <>(); PhantomReference<Object> phantomReference = new PhantomReference <Object>(o1, referenceQueue); System.out.println(o1); System.out.println(phantomReference.get()); System.out.println(referenceQueue.poll()); System.out.println(); o1 = null ; System.gc(); System.out.println(o1); System.out.println(phantomReference.get()); System.out.println(referenceQueue.poll()); } }
GCRoots和四大引用小总结
虚引用主要用来跟踪对象被垃圾回收器回收的活动。 虚引用与软引用和弱引用的一个区别在于: 虚引用必须和引用队列(ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。 参考: https://juejin.cn/post/6844903665241686029#heading-5
5.请谈谈你对OOM的认识 java.lang.StackOverflowError 没有边界的递归 case
1 2 3 4 5 6 7 8 9 10 11 12 package com.atguigu.jvm;public class StackOverflowErrorDemo { public static void main (String[] args) { stackOverflowError(); } private static void stackOverflowError () { stackOverflowError(); } }
1 2 3 Exception in thread "main" java.lang.StackOverflowError at com.atguigu.jvm.StackOverflowErrorDemo.stackOverflowError(StackOverflowErrorDemo.java:9) at com.atguigu.jvm.StackOverflowErrorDemo.stackOverflowError(StackOverflowErrorDemo.java:9)
面试题: java.lang.StackOverflowError属于错误(Error)还是异常(Exception)? 面试题: java.lang.StackOverflowError属于错误(Error)还是异常(Exception)? 答案: 错误
java.lang.OutOfMemoryError: Java heap space 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package com.atguigu.jvm;import java.util.Random;public class JavaHeapSpaceDemo { public static void main (String[] args) { byte [] bytes = new byte [50 * 1024 * 1024 ]; } }
java.lang.OutOfMemoryError: GC overhead limit exceeded
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 package com.atguigu.jvm;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 (Throwable e) { System.out.println("i = " + i); e.printStackTrace(); throw e; } } }
java.lang.OutOfMemoryError: Direct buffer memory
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 package com.atguigu.jvm;import java.nio.ByteBuffer;public class DirectBufferMemoryDemo { 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) { throw new RuntimeException (e); } ByteBuffer byteBuffer = ByteBuffer.allocateDirect(6 * 1024 * 1024 ); } }
java.lang.OutOfMemoryError: unable to create new native thread
自己做这个实验的时候, 记得不要用root, 创个普通用户, 这俩用户最大线程数不一样的, root会把linux干崩
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 [root:/opt/java-code-demo] UnableCreateNewThreadDemo.java [root:/opt/java-code-demo] package com.atguigu.jvm; /** * 高并发请求服务器时,经常出现如下异常:java.lang.OutOfMemoryError:unable to create new native thread * 准确的讲该native thread异常与对应的平台有关 * * 导致原因: * 1.你的应用创建了太多线程了,一个应用进程创建多个线程,超过系统承载极限 * 2.你的服务器并不允许你的应用程序创建这么多线程, linux系统默认允许单个进程可以创建的线程数是1024个, * 你的应用创建超过这个数量,就会报java.lang.0utOfMemoryError: unable to create new native thread * * 解决办法: * 1.想办法降低你应用程序创建线程的数量,分析应用是否真的需要创建这么多线程,如果不是,改代码将线程数降到最低 * 2.对于有的应用,确实需要创建很多线程,远超过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) { throw new RuntimeException(e); } }, String.valueOf(i)).start(); } } } 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 com.atguigu.jvm.UnableCreateNewThreadDemo.main(UnableCreateNewThreadDemo.java:27)
杀死进程
1 2 ps -ef | grep javakill -9 1234
服务器级别调参调优 查看当前用户可创建最大线程数
1 2 3 4 5 6 7 8 9 10 11 12 [roudoukou@xiamu java-code-demo]$ ulimit -u 4096 [roudoukou@xiamu java-code-demo]$ vim /etc/security/limits.d/20-nproc.conf * soft nproc 4096 root soft nproc unlimited
使用 java -XX:+PrintFlagsInitial
命令查看本机的初始化参数, -XX:MetaspaceSize为21810376B(大约20.8M)
导入依赖
1 2 3 4 5 6 <dependency > <groupId > cglib</groupId > <artifactId > cglib</artifactId > <version > 3.3.0</version > </dependency >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 package com.atguigu.jvm;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 { static class OOMTest { } 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 (Throwable e) { System.out.println("多少次后发生了异常: " + i); e.printStackTrace(); } } }
6.GC垃圾回收算法和垃圾收集器的关系?分别是什么请你谈谈 GC算法(引用计数/复制/标清/标整)是内存回收的方法论, 垃圾收集器就是算法落地实现 因为目前为止还没有完美的收集器出现, 更加没有万能的收集器, 只是针对具体应用最合适的收集器, 进行分代收集
4种主要垃圾收集器
1.串行垃圾回收器(Serial) 它为单线程环境设计且只有一个线程进行垃圾回收, 会暂停所有的用户线程, 所以不适合服务器环境
2.并行垃圾回收器(Parallel) 多个垃圾收集线程并行工作, 此时用户线程是暂停的, 适用于科学计算/大数据处理首台处理等弱交互场景
3.并发垃圾回收器(CMS) 用户线程和垃圾收集线程同时执行(不一定是并行, 可能交替执行), 不需要停顿用户线程 互联网公司多用它, 适用对响应时间有要求的场景
4.小总结
5.G1垃圾回收器 G1 垃圾回收器将堆内存分割成不同的区域然后并发的对其垃圾回收
7.怎么查看服务器默认的垃圾收集器是那个?生产上如何配置垃圾收集器的?谈谈你对垃圾收集器的理解? 怎么查看默认的垃圾收集器是哪个? JVM参数: java -XX:+PrintCommandLineFlags -version
1 2 3 4 5 6 $ java -XX:+PrintCommandLineFlags -version -XX:InitialHeapSize=265625408 -XX:MaxHeapSize=4250006528 -XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:+UseParallelGC java version "1.8.0_141" Java(TM) SE Runtime Environment (build 1.8.0_141-b15) Java HotSpot(TM) 64-Bit Server VM (build 25.141-b15, mixed mode)
由此可以得知: 默认的垃圾回收器就是: -XX:+UseParallelGC
默认的垃圾收集器有哪些? 图上的这六种, 以往还有一种叫 UseSerialOldGC
已经被废弃了 UseSerialGC,UseParallelGC,UseConcMarkSweepGC,UseParNewGC,UseParallelOldGC,UseG1GC
1 2 3 4 5 6 7 8 package com.atguigu.jvm;public class HelloGC { public static void main (String[] args) throws InterruptedException { System.out.println("HelloGC" ); Thread.sleep(Integer.MAX_VALUE); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 $ jps $ jinfo -flag UseSerialGC 1234 -XX:-UseSerialGC $ jinfo -flag UseParallelGC 1234 -XX:+UseParallelGC $ jps $ jinfo -flag UseSerialGC 1234 -XX:+UseSerialGC $ jinfo -flag UseParallelGC 1234 -XX:-UseParallelGC
垃圾收集器
部分参数预先说明
Server/Client模式分别是什么意思?
新生代 串行GC(Serial)/(Serial Copying)
代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package com.atguigu.jvm;import java.util.Random;public class GCDemo { public static void main (String[] args) { String str = "atguigu" ; try { while (true ) { str += str + new Random (111111 ) + new Random (222222 ); str.intern(); } } catch (Exception e) { throw new RuntimeException (e); } } }
参数:
1 2 -Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseSerialGC
结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 -XX:InitialHeapSize=10485760 -XX:MaxHeapSize=10485760 -XX:+PrintCommandLineFlags -XX:+PrintGCDetails -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:+UseSerialGC [GC (Allocation Failure) [DefNew: 2705K->320K(3072K), 0.0024652 secs] 2705K->991K(9920K), 0.0029408 secs] [Times: user=0.02 sys=0.00, real=0.00 secs] [GC (Allocation Failure) [DefNew: 2193K->228K(3072K), 0.0018383 secs] 2865K->1789K(9920K), 0.0018744 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [GC (Allocation Failure) [DefNew: 2103K->0K(3072K), 0.0012495 secs] 3665K->2701K(9920K), 0.0012911 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [GC (Allocation Failure) [DefNew: 964K->0K(3072K), 0.0008015 secs] 3666K->3613K(9920K), 0.0008289 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [GC (Allocation Failure) [DefNew: 1876K->0K(3072K), 0.0012697 secs] 5489K->5436K(9920K), 0.0012991 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [GC (Allocation Failure) [DefNew: 1876K->1876K(3072K), 0.0000214 secs][Tenured: 5436K->2757K(6848K), 0.0038461 secs] 7313K->2757K(9920K), [Metaspace: 3284K->3284K(1056768K)], 0.0041643 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [GC (Allocation Failure) [DefNew: 1877K->1877K(3072K), 0.0000230 secs][Tenured: 6404K->5492K(6848K), 0.0043658 secs] 8281K->5492K(9920K), [Metaspace: 3284K->3284K(1056768K)], 0.0044620 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] [GC (Allocation Failure) [DefNew: 1932K->1932K(3072K), 0.0000228 secs][Tenured: 5492K->2757K(6848K), 0.0041999 secs] 7425K->2757K(9920K), [Metaspace: 3284K->3284K(1056768K)], 0.0042884 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] [GC (Allocation Failure) [DefNew: 1877K->1877K(3072K), 0.0000186 secs][Tenured: 6405K->6114K(6848K), 0.0049234 secs] 8282K->6114K(9920K), [Metaspace: 3284K->3284K(1056768K)], 0.0050059 secs] [Times: user=0.01 sys=0.00, real=0.00 secs] [Full GC (Allocation Failure) [Tenured: 6114K->6095K(6848K), 0.0054355 secs] 6114K->6095K(9920K), [Metaspace: 3284K->3284K(1056768K)], 0.0054763 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] Heap def new generation total 3072K, used 109K [0x00000000ff600000, 0x00000000ff950000, 0x00000000ff950000) eden space 2752K, 3% used [0x00000000ff600000, 0x00000000ff61b500, 0x00000000ff8b0000) from space 320K, 0% used [0x00000000ff900000, 0x00000000ff900000, 0x00000000ff950000) to space 320K, 0% used [0x00000000ff8b0000, 0x00000000ff8b0000, 0x00000000ff900000) tenured generation total 6848K, used 6095K [0x00000000ff950000, 0x0000000100000000, 0x0000000100000000) the space 6848K, 89% used [0x00000000ff950000, 0x00000000fff43e58, 0x00000000fff44000, 0x0000000100000000) Metaspace used 3316K, capacity 4496K, committed 4864K, reserved 1056768K class space used 362K, capacity 388K, committed 512K, reserved 1048576K 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 java.lang.StringBuilder.append(StringBuilder.java:131) at com.atguigu.jvm.GCDemo.main(GCDemo.java:11)
并行GC(ParNew)
代码同上
参数:
1 2 -Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseParNewGC
结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 -XX:InitialHeapSize=10485760 -XX:MaxHeapSize=10485760 -XX:+PrintCommandLineFlags -XX:+PrintGCDetails -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:+UseParNewGC [GC (Allocation Failure) [ParNew: 2645K->318K(3072K), 0.0008312 secs] 2645K->1007K(9920K), 0.0013505 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [GC (Allocation Failure) [ParNew: 2222K->287K(3072K), 0.0009938 secs] 2911K->1668K(9920K), 0.0010280 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [GC (Allocation Failure) [ParNew: 2182K->15K(3072K), 0.0007004 secs] 3564K->3032K(9920K), 0.0007369 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [GC (Allocation Failure) [ParNew: 1016K->34K(3072K), 0.0010487 secs] 4033K->4874K(9920K), 0.0010808 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [GC (Allocation Failure) [ParNew: 1910K->30K(3072K), 0.0012064 secs] 6751K->6694K(9920K), 0.0012526 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [GC (Allocation Failure) [ParNew: 1906K->1906K(3072K), 0.0000220 secs][Tenured: 6664K->2634K(6848K), 0.0037589 secs] 8571K->2634K(9920K), [Metaspace: 3284K->3284K(1056768K)], 0.0042152 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [GC (Allocation Failure) [ParNew: 1876K->1876K(3072K), 0.0000200 secs][Tenured: 6282K->5370K(6848K), 0.0027142 secs] 8159K->5370K(9920K), [Metaspace: 3284K->3284K(1056768K)], 0.0028315 secs] [Times: user=0.02 sys=0.00, real=0.00 secs] [GC (Allocation Failure) [ParNew: 1932K->1932K(3072K), 0.0000162 secs][Tenured: 5370K->2634K(6848K), 0.0023289 secs] 7303K->2634K(9920K), [Metaspace: 3284K->3284K(1056768K)], 0.0023926 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [GC (Allocation Failure) [ParNew: 1877K->1877K(3072K), 0.0000233 secs][Tenured: 6282K->6114K(6848K), 0.0058202 secs] 8159K->6114K(9920K), [Metaspace: 3284K->3284K(1056768K)], 0.0059035 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] [Full GC (Allocation Failure) [Tenured: 6114K->6095K(6848K), 0.0035700 secs] 6114K->6095K(9920K), [Metaspace: 3284K->3284K(1056768K)], 0.0035997 secs] [Times: user=0.02 sys=0.00, real=0.00 secs] Heap par new generation total 3072K, used 109K [0x00000000ff600000, 0x00000000ff950000, 0x00000000ff950000) eden space 2752K, 3% used [0x00000000ff600000, 0x00000000ff61b4f0, 0x00000000ff8b0000) from space 320K, 0% used [0x00000000ff900000, 0x00000000ff900000, 0x00000000ff950000) to space 320K, 0% used [0x00000000ff8b0000, 0x00000000ff8b0000, 0x00000000ff900000) tenured generation total 6848K, used 6095K [0x00000000ff950000, 0x0000000100000000, 0x0000000100000000) the space 6848K, 89% used [0x00000000ff950000, 0x00000000fff43e58, 0x00000000fff44000, 0x0000000100000000) Metaspace used 3316K, capacity 4496K, committed 4864K, reserved 1056768K class space used 362K, capacity 388K, committed 512K, reserved 1048576K 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 java.lang.StringBuilder.append(StringBuilder.java:131) at com.atguigu.jvm.GCDemo.main(GCDemo.java:11) Java HotSpot(TM) 64-Bit Server VM warning: Using the ParNew young collector with the Serial old collector is deprecated and will likely be removed in a future release
并行回收GC(Parallel)/(Parallel Scavenge)
参数:
1 2 -Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseParallelGC
结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 -XX:InitialHeapSize=10485760 -XX:MaxHeapSize=10485760 -XX:+PrintCommandLineFlags -XX:+PrintGCDetails -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:+UseParallelGC [GC (Allocation Failure) [PSYoungGen: 2047K->485K(2560K)] 2047K->777K(9728K), 0.0022195 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [GC (Allocation Failure) [PSYoungGen: 2483K->483K(2560K)] 2775K->1391K(9728K), 0.0013570 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [GC (Allocation Failure) [PSYoungGen: 2360K->288K(2560K)] 3268K->1651K(9728K), 0.0009577 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [GC (Allocation Failure) [PSYoungGen: 1728K->400K(2560K)] 4916K->3587K(9728K), 0.0021833 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [GC (Allocation Failure) [PSYoungGen: 2263K->304K(2560K)] 7274K->6227K(9728K), 0.0012164 secs] [Times: user=0.01 sys=0.02, real=0.00 secs] [GC (Allocation Failure) [PSYoungGen: 304K->320K(1536K)] 6227K->6243K(8704K), 0.0015532 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [Full GC (Allocation Failure) [PSYoungGen: 320K->0K(1536K)] [ParOldGen: 5923K->3385K(7168K)] 6243K->3385K(8704K), [Metaspace: 3284K->3284K(1056768K)], 0.0100409 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] [GC (Allocation Failure) [PSYoungGen: 20K->32K(2048K)] 7053K->7065K(9216K), 0.0004494 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [Full GC (Ergonomics) [PSYoungGen: 32K->0K(2048K)] [ParOldGen: 7033K->5202K(7168K)] 7065K->5202K(9216K), [Metaspace: 3284K->3284K(1056768K)], 0.0065360 secs] [Times: user=0.02 sys=0.00, real=0.01 secs] [GC (Allocation Failure) [PSYoungGen: 40K->32K(2048K)] 7067K->7058K(9216K), 0.0003320 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [Full GC (Ergonomics) [PSYoungGen: 32K->0K(2048K)] [ParOldGen: 7026K->2466K(7168K)] 7058K->2466K(9216K), [Metaspace: 3284K->3284K(1056768K)], 0.0075694 secs] [Times: user=0.09 sys=0.00, real=0.01 secs] [GC (Allocation Failure) [PSYoungGen: 20K->0K(2048K)] 4310K->4290K(9216K), 0.0004211 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [GC (Allocation Failure) [PSYoungGen: 0K->0K(2048K)] 4290K->4290K(9216K), 0.0005201 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [Full GC (Allocation Failure) [PSYoungGen: 0K->0K(2048K)] [ParOldGen: 4290K->4290K(7168K)] 4290K->4290K(9216K), [Metaspace: 3284K->3284K(1056768K)], 0.0031239 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [GC (Allocation Failure) [PSYoungGen: 0K->0K(2048K)] 4290K->4290K(9216K), 0.0005009 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] [Full GC (Allocation Failure) [PSYoungGen: 0K->0K(2048K)] [ParOldGen: 4290K->4271K(7168K)] 4290K->4271K(9216K), [Metaspace: 3284K->3284K(1056768K)], 0.0119991 secs] [Times: user=0.06 sys=0.00, real=0.01 secs] Heap PSYoungGen total 2048K, used 61K [0x00000000ffd00000, 0x0000000100000000, 0x0000000100000000) eden space 1024K, 5% used [0x00000000ffd00000,0x00000000ffd0f428,0x00000000ffe00000) from space 1024K, 0% used [0x00000000ffe00000,0x00000000ffe00000,0x00000000fff00000) to space 1024K, 0% used [0x00000000fff00000,0x00000000fff00000,0x0000000100000000) ParOldGen total 7168K, used 4271K [0x00000000ff600000, 0x00000000ffd00000, 0x00000000ffd00000) object space 7168K, 59% used [0x00000000ff600000,0x00000000ffa2be20,0x00000000ffd00000) Metaspace used 3316K, capacity 4496K, committed 4864K, reserved 1056768K class space used 362K, capacity 388K, committed 512K, reserved 1048576K 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 com.atguigu.jvm.GCDemo.main(GCDemo.java:11)
老年代 串行GC(Serial Old)/(Serial MSC)
参数:
1 2 3 -Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseSerialOldGC
并行GC(Parallel Old)/(Parallel MSC)
参数:
1 2 3 4 -Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseParallelOldGC -Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags
并发标记清除GC(CMS)
4步过程: 1.初始化(CMS initial mark) 2.并发标记(CMS concurrent mark)和用户线程一起 3.重新标记(CMS remark) 4.并发清除(CMS concurrent sweep)和用户线程一起
四步概述:
优缺点: 1.优点: 并发收集低停顿 2.缺点: 并发执行, 对CPU资源压力大 采用的标记清除算法会导致大量碎片
参数:
1 2 3 -Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseConcMarkSweepGC
垃圾收集器配置代码总结 底层代码:
如何选择垃圾收集器
8.G1垃圾收集器 参数:
1 -Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseG1GC
运行:
以前收集器特点 年轻代和老年代是各自独立且连续的内存块; 年轻代收集使用单eden+SO+S1进行复制算法; 老年代收集必须扫描整个老年代区域; 都是以尽可能少而快速地执行GC为设计原则
G1是什么?
特点
底层原理 Region区域化垃圾收集器 最大好处是化整为零, 避免全内存扫描, 只需要按照区域来进行扫描即可
回收步骤
4步过程
case案例 代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package com.atguigu.jvm;import java.util.Random;public class GCDemo { public static void main (String[] args) { String str = "atguigu" ; try { while (true ) { str += str + new Random (111111 ) + new Random (222222 ); str.intern(); } } catch (Exception e) { throw new RuntimeException (e); } } }
参数
1 -Xms10m -Xmx10m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseG1GC
常用配置参数(了解)
和CMS相比的优势
小总结
JVMGC结合SpringBoot微服务优化 1.使用 mvn clean package
打包 2.在有包的路径下, 运行jar命令, 公式如下:
1 java -server jvm的各种参数 -jar
原先
1 java -jar springboot.jar
使用参数调优后
1 2 java -server -Xms1024m -Xmx1024m -XX:+UseG1GC -jar springboot.jar
9.生产环境服务器变慢,诊断思路和性能评估谈谈? 1.整机: top
2.CPU: vmstat 参考: https://www.cnblogs.com/ggjucheng/archive/2012/01/05/2312625.html
1 2 3 4 5 6 7 [01:29:01] root :: xiamu ➜ ~ » vmstat -n 2 3 procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu----- r b swpd free buff cache si so bi bo in cs us sy id wa st 1 0 0 993336 2156 1079908 0 0 7 12 106 171 0 0 100 0 0 0 0 0 993212 2156 1079908 0 0 0 0 387 649 0 0 100 0 0 1 0 0 993212 2156 1079908 0 0 0 0 391 645 0 0 100 0 0
拓展的命令 查看额外: 查看所有cpu核信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 [01:29:24] root :: xiamu ➜ ~ » mpstat -P ALL 2 Linux 3.10.0-1160.el7.x86_64 (xiamu) 2024年01月13日 _x86_64_ (4 CPU) 01时35分19秒 CPU %usr %nice %sys %iowait %irq %soft %steal %guest %gnice %idle 01时35分21秒 all 0.13 0.00 0.13 0.00 0.00 0.00 0.00 0.00 0.00 99.75 01时35分21秒 0 0.00 0.00 0.50 0.00 0.00 0.00 0.00 0.00 0.00 99.50 01时35分21秒 1 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 100.00 01时35分21秒 2 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 100.00 01时35分21秒 3 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 100.00 01时35分21秒 CPU %usr %nice %sys %iowait %irq %soft %steal %guest %gnice %idle 01时35分23秒 all 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 100.00 01时35分23秒 0 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 100.00 01时35分23秒 1 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 100.00 01时35分23秒 2 0.00 0.00 0.50 0.00 0.00 0.00 0.00 0.00 0.00 99.50 01时35分23秒 3 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 100.00
每个进程使用cpu的用量分解信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 [01:47:37] root :: xiamu ➜ /opt/java-code-demo » cat Dead.java 1 ↵ import java.util.*; public class Dead { public static void main(String[] args) { while (true ) { System.out.println(new Random().nextInt(10000)); } } } [01:47:40] root :: xiamu ➜ /opt/java-code-demo » javac Dead.java [01:47:40] root :: xiamu ➜ /opt/java-code-demo » java Dead λ ~/ ps -ef | grep java root 7498 7451 1 1月12 ? 00:09:21 /usr/lib/jvm/java-1.8.0-openjdk/bin/java -Xms512m -Xmx512m -Xmn256m -Dnacos.standalone=true -Djava.ext.dirs=/usr/lib/jvm/java-1.8.0-openjdk/jre/lib/ext:/usr/lib/jvm/java-1.8.0-openjdk/lib/ext:/home/nacos/plugins/health:/home/nacos/plugins/cmdb:/home/nacos/plugins/mysql -Xloggc:/home/nacos/logs/nacos_gc.log -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=100M -Dnacos.home=/home/nacos -jar /home/nacos/target/nacos-server.jar --spring.config.location=classpath:/,classpath:/config/,file:./,file:./config/,file:/home/nacos/conf/,/home/nacos/init.d/ --spring.config.name=application,custom --logging.config=/home/nacos/conf/nacos-logback.xml --server.max-http-header-size=524288 root 42360 7280 23 01:48 pts/1 00:00:07 java Dead root 42484 42402 0 01:49 pts/0 00:00:00 grep --color=auto --exclude-dir=.bzr --exclude-dir=CVS --exclude-dir=.git --exclude-dir=.hg --exclude-dir=.svn --exclude-dir=.idea --exclude-dir=.tox java λ ~/ pidstat -u 1 -p 42360 Linux 3.10.0-1160.el7.x86_64 (xiamu) 2024年01月13日 _x86_64_ (4 CPU) 01时49分22秒 UID PID %usr %system %guest %CPU CPU Command 01时49分23秒 0 42360 10.00 12.00 0.00 22.00 0 java 01时49分24秒 0 42360 9.00 14.00 0.00 23.00 0 java 01时49分25秒 0 42360 9.00 11.00 0.00 20.00 0 java 01时49分26秒 0 42360 8.00 14.00 0.00 22.00 0 java 01时49分27秒 0 42360 9.00 13.00 0.00 22.00 0 java 01时49分28秒 0 42360 9.00 12.00 0.00 21.00 0 java 01时49分29秒 0 42360 8.00 15.00 0.00 23.00 0 java 01时49分30秒 0 42360 9.00 13.00 0.00 22.00 0 java 01时49分31秒 0 42360 8.00 14.00 0.00 22.00 0 java 01时49分32秒 0 42360 8.91 13.86 0.00 22.77 0 java
3.内存: free
-m参数会四舍五入, 约等于4G, 只显示了3G
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 λ ~/ free total used free shared buff/cache available Mem: 3861300 1828680 928556 15040 1104064 1728592 Swap: 2097148 0 2097148 λ ~/ free -h total used free shared buff/cache available Mem: 3.7G 1.7G 907M 14M 1.1G 1.6G Swap: 2.0G 0B 2.0G λ ~/ free -g total used free shared buff/cache available Mem: 3 1 0 0 1 1 Swap: 1 0 1 λ ~/ free -m total used free shared buff/cache available Mem: 3770 1786 906 14 1078 1687 Swap: 2047 0 2047 λ ~/ pidstat -p 42360 -r 2 Linux 3.10.0-1160.el7.x86_64 (xiamu) 2024年01月13日 _x86_64_ (4 CPU) 01时52分42秒 UID PID minflt/s majflt/s VSZ RSS %MEM Command 01时52分44秒 0 42360 1.00 0.00 3404056 49504 1.28 java 01时52分46秒 0 42360 0.50 0.00 3404056 49504 1.28 java 01时52分48秒 0 42360 0.50 0.00 3404056 49504 1.28 java
4.硬盘: df 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 root@xiamu [11时32分31秒] [~] -> 文件系统 1K-块 已用 可用 已用% 挂载点 devtmpfs 1913584 0 1913584 0% /dev tmpfs 1930648 0 1930648 0% /dev/shm tmpfs 1930648 12804 1917844 1% /run tmpfs 1930648 0 1930648 0% /sys/fs/cgroup /dev/mapper/centos_roudoukou-root 38778880 16090904 22687976 42% / /dev/sda1 1038336 189220 849116 19% /boot tmpfs 386132 12 386120 1% /run/user/42 overlay 38778880 16090904 22687976 42% /var/lib/docker/overlay2/dc9d56c5fc9be6fa674ac3082e0bc4e11e752d3146268351eae5fa85cf8b54e4/merged overlay 38778880 16090904 22687976 42% /var/lib/docker/overlay2/e06aef924ef732cac3faa12ef3282d0033af42af84354a02ce636cf7057eed2b/merged overlay 38778880 16090904 22687976 42% /var/lib/docker/overlay2/f334a6550d72481f28ea577223e18da53e6c62a9580a6f2ca6bcad9540b3bfb5/merged tmpfs 386132 0 386132 0% /run/user/0 root@xiamu [11时32分48秒] [~] -> 文件系统 容量 已用 可用 已用% 挂载点 devtmpfs 1.9G 0 1.9G 0% /dev tmpfs 1.9G 0 1.9G 0% /dev/shm tmpfs 1.9G 13M 1.9G 1% /run tmpfs 1.9G 0 1.9G 0% /sys/fs/cgroup /dev/mapper/centos_roudoukou-root 37G 16G 22G 42% / /dev/sda1 1014M 185M 830M 19% /boot tmpfs 378M 12K 378M 1% /run/user/42 overlay 37G 16G 22G 42% /var/lib/docker/overlay2/dc9d56c5fc9be6fa674ac3082e0bc4e11e752d3146268351eae5fa85cf8b54e4/merged overlay 37G 16G 22G 42% /var/lib/docker/overlay2/e06aef924ef732cac3faa12ef3282d0033af42af84354a02ce636cf7057eed2b/merged overlay 37G 16G 22G 42% /var/lib/docker/overlay2/f334a6550d72481f28ea577223e18da53e6c62a9580a6f2ca6bcad9540b3bfb5/merged tmpfs 378M 0 378M 0% /run/user/0
5.磁盘IO: iostat 磁盘IO性能评估
查看额外 pidstat -d 采样间隔秒数 -p 进程号
6.网络IO: ifstat 默认本地没有,
查看网络IO ifstat
10.假如生产环境出现CPU占用过高,请谈谈你的分析思路和定位
定位到具体线程或代码: (ps -mp 进程 -o THREAD,tid,time) 参数解释: -m显示所有的线程 -p pid 进程使用cpu时间 -o 该参数后是用户自定义格式
printf “%x\n” 有问题的线程ID
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 [root@xiamu ~ ]$ jps -l 3136 -- process information unavailable 88579 Dead 88750 sun.tools.jps.Jps [root@xiamu ~ ]$ ps -ef | grep java | grep -v grep root 7498 7451 1 01:20 ? 00:16:46 /usr/lib/jvm/java-1.8.0-openjdk/bin/java -Xms512m -Xmx512m -Xmn256m -Dnacos.standalone=true -Djava.ext.dirs=/usr/lib/jvm/java-1.8.0-openjdk/jre/lib/ext:/usr/lib/jvm/java-1.8.0-openjdk/lib/ext:/home/nacos/plugins/health:/home/nacos/plugins/cmdb:/home/nacos/plugins/mysql -Xloggc:/home/nacos/logs/nacos_gc.log -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=100M -Dnacos.home=/home/nacos -jar /home/nacos/target/nacos-server.jar --spring.config.location=classpath:/,classpath:/config/,file:./,file:./config/,file:/home/nacos/conf/,/home/nacos/init.d/ --spring.config.name=application,custom --logging.config=/home/nacos/conf/nacos-logback.xml --server.max-http-header-size=524288 root 88579 88378 75 23:52 pts/1 00:01:16 java Dead [root@xiamu ~ ]$ ps -mp 88579 -o THREAD,tid,time USER %CPU PRI SCNT WCHAN USER SYSTEM TID TIME root 75.2 - - - - - - 00:02:06 root 0.0 19 - futex_ - - 88579 00:00:00 root 73.5 19 - - - - 88580 00:02:03 root 0.2 19 - futex_ - - 88581 00:00:00 root 0.3 19 - futex_ - - 88582 00:00:00 root 0.3 19 - futex_ - - 88583 00:00:00 root 0.2 19 - futex_ - - 88584 00:00:00 root 0.1 19 - futex_ - - 88585 00:00:00 root 0.0 19 - futex_ - - 88586 00:00:00 root 0.0 19 - futex_ - - 88587 00:00:00 root 0.0 19 - futex_ - - 88588 00:00:00 root 0.0 19 - futex_ - - 88589 00:00:00 root 0.0 19 - futex_ - - 88590 00:00:00 root 0.0 19 - futex_ - - 88591 00:00:00 root 0.0 19 - futex_ - - 88592 00:00:00 root 0.1 19 - futex_ - - 88593 00:00:00 [root@xiamu ~ ]$ printf "%x\n" 88580 15a04 [root@xiamu ~ ]$ jstack 88579 | grep 15a04 -A60"main" java.lang.Thread.State: RUNNABLE at java.io.FileOutputStream.writeBytes(Native Method) at java.io.FileOutputStream.write(FileOutputStream.java:326) at java.io.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:82) at java.io.BufferedOutputStream.flush(BufferedOutputStream.java:140) - locked <0x00000000c503cb70> (a java.io.BufferedOutputStream) at java.io.PrintStream.write(PrintStream.java:482) - locked <0x00000000c5005378> (a java.io.PrintStream) at sun.nio.cs.StreamEncoder.writeBytes(StreamEncoder.java:221) at sun.nio.cs.StreamEncoder.implFlushBuffer(StreamEncoder.java:291) at sun.nio.cs.StreamEncoder.flushBuffer(StreamEncoder.java:104) - locked <0x00000000c5005330> (a java.io.OutputStreamWriter) at java.io.OutputStreamWriter.flushBuffer(OutputStreamWriter.java:185) at java.io.PrintStream.write(PrintStream.java:527) - eliminated <0x00000000c5005378> (a java.io.PrintStream) at java.io.PrintStream.print (PrintStream.java:597) at java.io.PrintStream.println(PrintStream.java:736) - locked <0x00000000c5005378> (a java.io.PrintStream) at Dead.main(Dead.java:6)"VM Thread" os_prio=0 tid=0x00007f7414077800 nid=0x15a09 runnable"GC task thread#0 (ParallelGC)" os_prio=0 tid=0x00007f741401e800 nid=0x15a05 runnable"GC task thread#1 (ParallelGC)" os_prio=0 tid=0x00007f7414020800 nid=0x15a06 runnable"GC task thread#2 (ParallelGC)" os_prio=0 tid=0x00007f7414022000 nid=0x15a07 runnable"GC task thread#3 (ParallelGC)" os_prio=0 tid=0x00007f7414024000 nid=0x15a08 runnable"VM Periodic Task Thread" os_prio=0 tid=0x00007f74140e5800 nid=0x15a11 waiting on condition JNI global references: 5
由此可以定位到具体代码: at Dead.main(Dead.java:6)
11.对于JDK自带的JVM监控和性能分析工具用过哪些? 一般你是怎么用的? github骚操作 常用词含义
in关键词限制搜索范围
stars或fork数量关键词去查找
awesome加强搜索
高亮显示某一行代码
项目内搜索
https://docs.github.com/en/get-started/accessibility/keyboard-shortcuts
搜索某个地区内的大佬
栈管运行, 堆管存储 理论(技术case + 生活case) 代码 小总结 线程操纵资源类, 判断干活唤醒通知, 严防多线程并发状态下的虚假唤醒 物以类聚人以群分 任何人在他牛逼之前定会有段苦逼的岁月但请你像傻逼一样的坚持终于会有一个像宋红康一样装B的结果 show me you code
天上飞的理念, 必有落地的实现