重庆网站制作定制,网站建设收获,英文版wordpress,深圳网址导航预览
并发
问题详解请谈谈你对volatile的理解linkCAS你知道吗#xff1f;link原子类Atomiclnteger的ABA问题谈谈?原子更新引用知道吗#xff1f;link我们知道ArrayList是线程不安全#xff0c;请编码写一个不安全的案例并给出解决方案link公平锁/非公平锁/可重入锁/递归锁…预览
并发
问题详解请谈谈你对volatile的理解linkCAS你知道吗link原子类Atomiclnteger的ABA问题谈谈?原子更新引用知道吗link我们知道ArrayList是线程不安全请编码写一个不安全的案例并给出解决方案link公平锁/非公平锁/可重入锁/递归锁/自旋锁谈谈你的理解请手写一个自旋锁linkCountDownLatch/CyclicBarrier/Semaphore使用过吗link阻塞队列知道吗link线程池用过吗ThreadPoolExecutor谈谈你的理解link线程池用过吗生产上你如何设置合理参数link死锁编码及定位分析link
JVM
问题详解JVM垃圾回收的时候如何确定垃圾是否知道什么是GC Rootslink你说你做过JVM调优和参数配置请问如何盘点查看JVM系统默认值link你平时工作用过的JVM常用基本配置参数有哪些link强引用、软引用、弱引用、虚引用分别是什么请谈谈你对OOM的认识linkGC垃圾回收算法和垃圾收集器的关系分别是什么请你谈谈怎么查看服务器默认的垃圾收集器是那个link生产上如何配置垃圾收集器的link谈谈你对垃圾收集器的理解G1垃圾收集器link生产环境服务器变慢诊断思路和性能评估谈谈link假如生产环境出现CPU占用过高请谈谈你的分析思路和定位link对于JDK自带的JVM监控和性能分析工具用过哪些?一般你是怎么用的link---预览01_本课程前提要求和说明02_volatile是什么03_JMM内存模型之可见性04_可见性的代码验证说明05_volatile不保证原子性06_volatile不保证原子性理论解释07_volatile不保证原子性问题解决08_volatile指令重排案例109_volatile指令重排案例210_单例模式在多线程环境下可能存在安全问题11_单例模式volatile分析12_CAS是什么13_CAS底层原理-上14_CAS底层原理-下15_CAS缺点16_ABA问题17_AtomicReference原子引用18_AtomicStampedReference版本号原子引用19_ABA问题的解决20_集合类不安全之并发修改异常21_集合类不安全之写时复制22_集合类不安全之Set23_集合类不安全之Map24_TransferValue醒脑小练习25_java锁之公平和非公平锁26_java锁之可重入锁和递归锁理论知识27_java锁之可重入锁和递归锁代码验证28_java锁之自旋锁理论知识29_java锁之自旋锁代码验证30_java锁之读写锁理论知识31_java锁之读写锁代码验证32_CountDownLatch33_CyclicBarrierDemo34_SemaphoreDemo35_阻塞队列理论36_阻塞队列接口结构和实现类37_阻塞队列api之抛出异常组38_阻塞队列api之返回布尔值组39_阻塞队列api之阻塞和超时控制40_阻塞队列之同步SynchronousQueue队列41_线程通信之生产者消费者传统版42_Synchronized和Lock有什么区别43_锁绑定多个条件Condition44_线程通信之生产者消费者阻塞队列版45_Callable接口46_线程池使用及优势47_线程池3个常用方式48_线程池7大参数入门简介49_线程池7大参数深入介绍50_线程池底层工作原理51_线程池的4种拒绝策略理论简介52_线程池实际中使用哪一个53_线程池的手写改造和拒绝策略54_线程池配置合理线程数55_死锁编码及定位分析56_JVMGC下半场技术加强说明和前提知识要求57_JVMGC快速回顾复习串讲58_谈谈你对GCRoots的理解59_JVM的标配参数和X参数60_JVM的XX参数之布尔类型61_JVM的XX参数之设值类型62_JVM的XX参数之XmsXmx坑题63_JVM盘点家底查看初始默认值64_JVM盘点家底查看修改变更值65_堆内存初始大小快速复习66_常用基础参数栈内存Xss讲解67_常用基础参数元空间MetaspaceSize讲解68_常用基础参数PrintGCDetails回收前后对比讲解69_常用基础参数SurvivorRatio讲解70_常用基础参数NewRatio讲解71_常用基础参数MaxTenuringThreshold讲解72_强引用Reference73_软引用SoftReference74_弱引用WeakReference75_软引用和弱引用的适用场景76_WeakHashMap案例演示和解析77_虚引用简介78_ReferenceQueue引用队列介79_虚引用PhantomReference80_GCRoots和四大引用小总结81_SOFE之StackOverflowError82_OOM之Java heap space83_OOM之GC overhead limit exceeded84_OOM之Direct buffer memory85_OOM之unable to create new native thread故障演示86_OOM之unable to create new native thread上限调整87_OOM之Metaspace88_垃圾收集器回收种类89_串行并行并发G1四大垃圾回收方式90_如何查看默认的垃圾收集器91_JVM默认的垃圾收集器有哪些92_GC之7大垃圾收集器概述93_GC之约定参数说明94_GC之Serial收集器95_GC之ParNew收集器96_GC之Parallel收集器97_GC之ParallelOld收集器98_GC之CMS收集器99_GC之SerialOld收集器100_GC之如何选择垃圾收集器101_GC之G1收集器102_GC之G1底层原理103_GC之G1参数配置及和CMS的比较104_JVMGC结合SpringBoot微服务优化简介105_Linux命令之top106_Linux之cpu查看vmstat107_Linux之cpu查看pidstat108_Linux之内存查看free和pidstat109_Linux之硬盘查看df110_Linux之磁盘IO查看iostat和pidstat111_Linux之网络IO查看ifstat112_CPU占用过高的定位分析思路113_GitHub骚操作之开启114_GitHub骚操作之常用词115_GitHub骚操作之in限制搜索116_GitHub骚操作之star和fork范围搜索117_GitHub骚操作之awesome搜索118_GitHub骚操作之#L数字119_GitHub骚操作之T搜索120_GitHub骚操作之搜索区域活跃用户--
01_本课程前提要求和说明
教学视频
一些大厂的面试题
蚂蚁花呗一面
Java容器有哪些哪些是同步容器,哪些是并发容器ArrayList和LinkedList的插入和访问的时间复杂度java反射原理注解原理新生代分为几个区使用什么算法进行垃圾回收为什么使用这个算法HashMap在什么情况下会扩容或者有哪些操作会导致扩容HashMap push方法的执行过程HashMap检测到hash冲突后将元素插入在链表的末尾还是开头1.8还采用了红黑树讲讲红黑树的特性为什么人家一定要用红黑树而不是AVL、B树之类的https和http区别有没有用过其他安全传输手段线程池的工作原理几个重要参数然后给了具体几个参数分析线程池会怎么做最后问阻塞队列的作用是什么linux怎么查看系统负载情况请详细描述springmvc处理请求全流程spring 一个bean装配的过程讲一讲AtomicInteger为什么要用CAS而不是synchronized
美团一面
最近做的比较熟悉的项目是哪个画一下项目技术架构图。JVM老年代和新生代的比例YGC和FGC发生的具体场景jstackjmapjutil分别的意义如何线上排查JVM的相关问题线程池的构造类的方法的5个参数的具体意义单机上一个线程池正在处理服务如果忽然断电怎么办正在处理和阻塞队列里的请求怎么处理)使用无界阻塞队列会出现什么问题接口如何处理重复请求
百度一面
介绍一下集合框架hashmap hastable 底层实现什么区别hashtable和concurrenthashtable呢hashmap和treemap什么区别低层数据结构是什么线程池用过吗都有什么参数底层如何实现的sychnized和Lock什么区别sychnize 什么情况情况是对象锁什么时候是全局锁为什么ThreadLocal 是什么底层如何实现写一个例子呗volitile的工作原理cas知道吗如何实现的请用至少四种写法写一个单例模式请介绍一下JVM内存模型用过什么垃圾回收器都说说呗线上发送频繁full gc如何处理CPU使用率过高怎么办如何定位问题如何解决说一下解决思路和处理方法知道字节码吗字节码都有哪些Integer x 5,int y 5比较x y 都经过哪些步骤讲讲类加载机制呗都有哪些类加载器这些类加载器都加载哪些文件手写一下类加载Demo知道osgi吗他是如何实现的请问你做过哪些JVM优化使用什么方法达到什么效果classforName(“java.lang.String”)和String classgetClassLoader() LoadClass(“java.lang.String”)什么区别啊
今日头条
HashMap如果一直put元素会怎么样 hashcode全都相同如何ApplicationContext的初始化过程GC 用什么收集器收集的过程如何哪些部分可以作为GC RootVolatile关键字指令重排序有什么意义 synchronied,怎么用Redis数据结构有哪些如何实现sorted set并发包里的原子类有哪些怎么实现MvSql索引是什么数据结构 B tree有什么特点优点是什么慢查询怎么优化项目: cache各部分职责有哪些优化点
京东金融面试
Dubbo超时重试Dubbo超时时间设置如何保障请求执行顺序分布式事务与分布式锁(扣款不要出现负数)分布式Session设置执行某操作前50次成功第51次失败a全部回滚b前50次提交第51次抛异常ab场景分别如何设计Spring (传播特性)Zookeeper有却些作用JVM内存模型数据库垂直和水平拆分MyBatis如何分页;如何设置缓存;MySQL分页
蚂蚁金服二面
自我介绍、工作经历、技术栈项目中你学到了什么技术(把三项目具体描述了很久)微服务划分的粒度微服务的高可用怎么保证的常用的负载均衡该怎么用你能说下吗网关能够为后端服务带来哪些好处Spring Bean的生命周期HashSet是不是线程安全的为什么不是线程安全的Java 中有哪些线程安全的MapConcurrenthashmap 是怎么做到线程安全的HashTable你了解过吗如何保证线程安全问题synchronized、lockvolatile 的原子性问题为什么i这种不支持原子性﹖从计算机原理的设计来讲下不能保证原子性的原因happens before 原理cas操作lock和 synchronized 的区别公平锁和非公平锁Java读写锁读写锁设计主要解决什么问题
02_volatile是什么
volatile是JVM提供的轻量级的同步机制
保证可见性不保证原子性禁止指令重排保证有序性
03_JMM内存模型之可见性
JMMJava内存模型Java Memory Model简称JMM本身是一种抽象的概念并不真实存在它描述的是一组规则或规范通过这组规范定义了程序中各个变量包括实例字段静态字段和构成数组对象的元素的访问方式。
JMM关于同步的规定
线程解锁前必须把共享变量的值刷新回主内存线程加锁前必须读取主内存的最新值到自己的工作内存加锁解锁是同一把锁
由于JVM运行程序的实体是线程而每个线程创建时JVM都会为其创建一个工作内存有些地方称为栈空间工作内存是每个线程的私有数据区域而Java内存模型中规定所有变量都存储在主内存主内存是共享内存区域所有线程都可以访问但线程对变量的操作读取赋值等必须在工作内存中进行首先要将变量从主内存拷贝的自己的工作内存空间然后对变量进行操作操作完成后再将变量写回主内存不能直接操作主内存中的变量各个线程中的工作内存中存储着主内存中的变量副本拷贝因此不同的线程间无法访问对方的工作内存线程间的通信传值必须通过主内存来完成其简要访问过程如下图 可见性
通过前面对JMM的介绍我们知道各个线程对主内存中共享变量的操作都是各个线程各自拷贝到自己的工作内存进行操作后再写回到主内存中的。
这就可能存在一个线程AAA修改了共享变量X的值但还未写回主内存时另外一个线程BBB又对主内存中同一个共享变量X进行操作但此时A线程工作内存中共享变量x对线程B来说并不可见这种工作内存与主内存同步延迟现象就造成了可见性问题
04_可见性的代码验证说明
import java.util.concurrent.TimeUnit;/*** 假设是主物理内存*/
class MyData {//volatile int number 0;int number 0;public void addTo60() {this.number 60;}
}/*** 验证volatile的可见性* 1. 假设int number 0 number变量之前没有添加volatile关键字修饰*/
public class VolatileDemo {public static void main(String args []) {// 资源类MyData myData new MyData();// AAA线程 实现了Runnable接口的lambda表达式new Thread(() - {System.out.println(Thread.currentThread().getName() \t come in);// 线程睡眠3秒假设在进行运算try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace();}// 修改number的值myData.addTo60();// 输出修改后的值System.out.println(Thread.currentThread().getName() \t update number value: myData.number);}, AAA).start();// main线程就一直在这里等待循环直到number的值不等于零while(myData.number 0) {}// 按道理这个值是不可能打印出来的因为主线程运行的时候number的值为0所以一直在循环// 如果能输出这句话说明AAA线程在睡眠3秒后更新的number的值重新写入到主内存并被main线程感知到了System.out.println(Thread.currentThread().getName() \t mission is over);}
}由于没有volatile修饰MyData类的成员变量numbermain线程将会卡在while(myData.number 0) {}不能正常结束。若想正确结束用volatile修饰MyData类的成员变量number吧。
volatile类比
没有volatile修饰变量效果相当于A同学拷贝了老师同一课件A同学对课件进一步的总结归纳形成自己的课件这就与老师的课件不同了。
有volatile修饰变量效果相当于A同学拷贝了老师同一课件A同学对课件进一步的总结归纳形成自己的课件并且与老师分享老师认可A同学修改后的课件并用它来作下一届的课件。
05_volatile不保证原子性
原子性指的是什么意思
不可分割完整性也即某个线程正在做某个具体业务时中间不可以被加塞或者被分割。需要整体完整要么同时成功要么同时失败。
volatile不保证原子性案例演示
class MyData2 {/*** volatile 修饰的关键字是为了增加 主线程和线程之间的可见性只要有一个线程修改了内存中的值其它线程也能马上感知*/volatile int number 0;public void addPlusPlus() {number ;}
}public class VolatileAtomicityDemo {public static void main(String[] args) {MyData2 myData new MyData2();// 创建10个线程线程里面进行1000次循环for (int i 0; i 20; i) {new Thread(() - {// 里面for (int j 0; j 1000; j) {myData.addPlusPlus();}}, String.valueOf(i)).start();}// 需要等待上面20个线程都计算完成后在用main线程取得最终的结果值// 这里判断线程数是否大于2为什么是2因为默认是有两个线程的一个main线程一个gc线程while(Thread.activeCount() 2) {// yield表示不执行Thread.yield();}// 查看最终的值// 假设volatile保证原子性那么输出的值应该为 20 * 1000 20000System.out.println(Thread.currentThread().getName() \t finally number value: myData.number);}}
最后的结果总是小于20000。
06_volatile不保证原子性理论解释
number在多线程下是非线程安全的。
我们可以将代码编译成字节码可看出number被编译成3条指令。 假设我们没有加 synchronized那么第一步就可能存在着三个线程同时通过getfield命令拿到主存中的 n值然后三个线程各自在自己的工作内存中进行加1操作但他们并发进行 iadd 命令的时候因为只能一个进行写所以其它操作会被挂起假设1线程先进行了写操作在写完后volatile的可见性应该需要告诉其它两个线程主内存的值已经被修改了但是因为太快了其它两个线程陆续执行 iadd命令进行写入操作这就造成了其他线程没有接受到主内存n的改变从而覆盖了原来的值出现写丢失这样也就让最终的结果少于20000。
07_volatile不保证原子性问题解决
可加synchronized解决但它是重量级同步机制性能上有所顾虑。
如何不加synchronized解决number在多线程下是非线程安全的问题使用AtomicInteger。
import java.util.concurrent.atomic.AtomicInteger;class MyData2 {/*** volatile 修饰的关键字是为了增加 主线程和线程之间的可见性只要有一个线程修改了内存中的值其它线程也能马上感知*/volatile int number 0;AtomicInteger number2 new AtomicInteger();public void addPlusPlus() {number ;}public void addPlusPlus2() {number2.getAndIncrement();}
}public class VolatileAtomicityDemo {public static void main(String[] args) {MyData2 myData new MyData2();// 创建10个线程线程里面进行1000次循环for (int i 0; i 20; i) {new Thread(() - {// 里面for (int j 0; j 1000; j) {myData.addPlusPlus();myData.addPlusPlus2();}}, String.valueOf(i)).start();}// 需要等待上面20个线程都计算完成后在用main线程取得最终的结果值// 这里判断线程数是否大于2为什么是2因为默认是有两个线程的一个main线程一个gc线程while(Thread.activeCount() 2) {// yield表示不执行Thread.yield();}// 查看最终的值// 假设volatile保证原子性那么输出的值应该为 20 * 1000 20000System.out.println(Thread.currentThread().getName() \t finally number value: myData.number);System.out.println(Thread.currentThread().getName() \t finally number2 value: myData.number2);}
}输出结果为
main finally number value: 18766
main finally number2 value: 2000008_volatile指令重排案例1
计算机在执行程序时为了提高性能编译器和处理器的常常会对指令做重排一般分以下3种 单线程环境里面确保程序最终执行结果和代码顺序执行的结果一致。
处理器在进行重排序时必须要考虑指令之间的数据依赖性
多线程环境中线程交替执行由于编译器优化重排的存在两个线程中使用的变量能否保证一致性是无法确定的结果无法预测。
重排案例
public void mySort{int x 11;//语句1int y 12;//语句2× × 5;//语句3y x * x;//语句4
}可重排序列
123421341324
问题请问语句4可以重排后变成第一个条吗答不能。
重排案例2
int a,b,x,y 0
线程1线程2x a;y b;b 1;a 2;x 0; y 0
如果编译器对这段程序代码执行重排优化后可能出现下列情况
线程1线程2b 1;a 2;x a;y b;x 2; y 1
这也就说明在多线程环境下由于编译器优化重排的存在两个线程中使用的变量能否保证一致性是无法确定的。
09_volatile指令重排案例2
观察以下程序
public class ReSortSeqDemo{int a 0;boolean flag false;public void method01(){a 1;//语句1flag true;//语句2}public void method02(){if(flag){a a 5; //语句3}System.out.println(retValue: a);//可能是6或1或5或0}}多线程环境中线程交替执行method01()和method02()由于编译器优化重排的存在两个线程中使用的变量能否保证一致性是无法确定的结果无法预测。
禁止指令重排小总结
volatile实现禁止指令重排优化从而避免多线程环境下程序出现乱序执行的现象
先了解一个概念内存屏障(Memory Barrier又称内存栅栏是一个CPU指令它的作用有两个:
保证特定操作的执行顺序保证某些变量的内存可见性利用该特性实现volatile的内存可见性。
由于编译器和处理器都能执行指令重排优化。如果在指令间插入一条Memory Barrier则会告诉编译器和CPU不管什么指令都不能和这条Memory Barrier指令重排序也就是说通过插入内存屏障禁止在内存屏障前后的指令执行重排序优化。内存屏障另外一个作用是强制刷出各种CPU的缓存数据因此任何CPU上的线程都能读取到这些数据的最新版本。
对volatile变量进行写操作时会在写操作后加入一条store屏障指令将工作内存中的共享变量值刷新回到主内存。 对Volatile变量进行读操作时会在读操作前加入一条load屏障指令从主内存中读取共享变量。 线性安全性获得保证 工作内存与主内存同步延迟现象导致的可见性问题 - 可以使用synchronized或volatile关键字解决它们都可以使一个线程修改后的变量立即对其他线程可见。 对于指令重排导致的可见性问题和有序性问题 - 可以利用volatile关键字解决因为volatile的另外一个作用就是禁止重排序优化。
10_单例模式在多线程环境下可能存在安全问题
懒汉单例模式
public class SingletonDemo {private static SingletonDemo instance null;private SingletonDemo () {System.out.println(Thread.currentThread().getName() \t 我是构造方法SingletonDemo);}public static SingletonDemo getInstance() {if(instance null) {instance new SingletonDemo();}return instance;}public static void main(String[] args) {// 这里的 是比较内存地址System.out.println(SingletonDemo.getInstance() SingletonDemo.getInstance());System.out.println(SingletonDemo.getInstance() SingletonDemo.getInstance());System.out.println(SingletonDemo.getInstance() SingletonDemo.getInstance());System.out.println(SingletonDemo.getInstance() SingletonDemo.getInstance());}
}输出结果
main 我是构造方法singletonDemo
true
true
true
true但是在多线程环境运行上述代码能保证单例吗
public class SingletonDemo {private static SingletonDemo instance null;private SingletonDemo () {System.out.println(Thread.currentThread().getName() \t 我是构造方法SingletonDemo);}public static SingletonDemo getInstance() {if(instance null) {instance new SingletonDemo();}return instance;}public static void main(String[] args) {for (int i 0; i 10; i) {new Thread(() - {SingletonDemo.getInstance();}, String.valueOf(i)).start();}}
}输出结果
4 我是构造方法SingletonDemo
2 我是构造方法SingletonDemo
5 我是构造方法SingletonDemo
6 我是构造方法SingletonDemo
0 我是构造方法SingletonDemo
3 我是构造方法SingletonDemo
1 我是构造方法SingletonDemo显然不能保证单例。
解决方法之一用synchronized修饰方法getInstance()但它属重量级同步机制使用时慎重。
public synchronized static SingletonDemo getInstance() {if(instance null) {instance new SingletonDemo();}return instance;
}11_单例模式volatile分析
解决方法之二DCLDouble Check Lock双端检锁机制
public class SingletonDemo{private SingletonDemo(){}private volatile static SingletonDemo instance null;public static SingletonDemo getInstance() {if(instance null) {synchronized(SingletonDemo.class){if(instance null){instance new SingletonDemo(); }}}return instance;}
}DCL中volatile解析
原因在于某一个线程执行到第一次检测读取到的instance不为null时instance的引用对象可能没有完成初始化。instance new SingletonDemo();可以分为以下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不为null时由于instance实例未必已初始化完成也就造成了线程安全问题。
12_CAS是什么
Compare And Set
示例程序
public class CASDemo{public static void main(string[] args){AtomicInteger atomicInteger new AtomicInteger(5);// mian do thing. . . . ..System.out.println(atomicInteger.compareAndSet(5, 2019)\t current data: atomicInteger.get());System.out.println(atomicInteger.compareAndset(5, 1024)\t current data: atomicInteger.get());}
}输出结果为
true 2019
false 201913_CAS底层原理-上
Cas底层原理如果知道谈谈你对UnSafe的理解
atomiclnteger.getAndIncrement();源码
public class AtomicInteger extends Number implements java.io.Serializable {private static final long serialVersionUID 6214790243416807050L;// setup to use Unsafe.compareAndSwapInt for updatesprivate static final Unsafe unsafe Unsafe.getUnsafe();private static final long valueOffset;static {try {valueOffset unsafe.objectFieldOffset(AtomicInteger.class.getDeclaredField(value));} catch (Exception ex) { throw new Error(ex); }}private volatile int value;/*** Creates a new AtomicInteger with the given initial value.** param initialValue the initial value*/public AtomicInteger(int initialValue) {value initialValue;}/*** Creates a new AtomicInteger with initial value {code 0}.*/public AtomicInteger() {}.../*** Atomically increments by one the current value.** return the previous value*/public final int getAndIncrement() {return unsafe.getAndAddInt(this, valueOffset, 1);}...
}
UnSafe
1 Unsafe
是CAS的核心类由于Java方法无法直接访问底层系统需要通过本地native方法来访问Unsafe相当于一个后门基于该类可以直接操作特定内存的数据。Unsafe类存在于sun.misc包中其内部方法操作可以像C的指针一样直接操作内存因为Java中CAS操作的执行依赖于Unsafe类的方法。
注意Unsafe类中的所有方法都是native修饰的也就是说Unsafe类中的方法都直接调用操作系统底层资源执行相应任务。
2 变量valueOffset表示该变量值在内存中的偏移地址因为Unsafe就是根据内存偏移地址获取数据的。
3 变量value用volatile修饰保证了多线程之间的内存可见性。
CAS是什么
CAS的全称为Compare-And-Swap它是一条CPU并发原语。
它的功能是判断内存某个位置的值是否为预期值如果是则更改为新的值这个过程是原子的。
CAS并发原语体现在JAVA语言中就是sun.misc.Unsafe类中的各个方法。调用UnSafe类中的CAS方法JVM会帮我们实现出CAS汇编指令。这是一种完全依赖于硬件的功能通过它实现了原子操作。再次强调由于CAS是一种系统原语原语属于操作系统用语范畴是由若干条指令组成的用于完成某个功能的一个过程并且原语的执行必须是连续的在执行过程中不允许被中断也就是说CAS是一条CPU的原子指令不会造成所谓的数据不一致问题。原子性 14_CAS底层原理-下
继续上一节
UnSafe.getAndAddInt()源码解释
var1 AtomicInteger对象本身。var2 该对象值得引用地址。var4 需要变动的数量。var5是用过var1var2找出的主内存中真实的值。用该对象当前的值与var5比较 如果相同更新var5var4并且返回true,如果不同继续取值然后再比较直到更新完成。
假设线程A和线程B两个线程同时执行getAndAddInt操作分别跑在不同CPU上)
Atomiclnteger里面的value原始值为3即主内存中Atomiclnteger的value为3根据JMM模型线程A和线程B各自持有一份值为3的value的副本分别到各自的工作内存。线程A通过getIntVolatile(var1, var2)拿到value值3这时线程A被挂起。线程B也通过getintVolatile(var1, var2)方法获取到value值3此时刚好线程B没有被挂起并执行compareAndSwapInt方法比较内存值也为3成功修改内存值为4线程B打完收工一切OK。这时线程A恢复执行compareAndSwapInt方法比较发现自己手里的值数字3和主内存的值数字4不一致说明该值己经被其它线程抢先一步修改过了那A线程本次修改失败只能重新读取重新来一遍了。线程A重新获取value值因为变量value被volatile修饰所以其它线程对它的修改线程A总是能够看到线程A继续执行compareAndSwaplnt进行比较替换直到成功。
底层汇编
Unsafe类中的compareAndSwapInt是一个本地方法该方法的实现位于unsafe.cpp中。
UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSwapInt(JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jint e, jint x)
UnsafeWrapper(Unsafe_CompareAndSwaplnt);
oop p JNlHandles::resolve(obj);
jint* addr (jint *)index_oop_from_field_offset_long(p, offset);
return (jint)(Atomic::cmpxchg(x, addr, e)) e;
UNSAFE_END
//先想办法拿到变量value在内存中的地址。
//通过Atomic::cmpxchg实现比较替换其中参数x是即将更新的值参数e是原内存的值。小结
CAS指令
CAS有3个操作数内存值V旧的预期值A要修改的更新值B。 当且仅当预期值A和内存值V相同时将内存值V修改为B否则什么都不做。
15_CAS缺点
循环时间长开销很大
// ursafe.getAndAddInt
public final int getAndAddInt(Object var1, long var2, int var4){int var5;do {var5 this.getIntVolatile(var1, var2);}while(!this.compareAndSwapInt(varl, var2, var5var5 var4));return var5;
}我们可以看到getAndAddInt方法执行时有个do while如果CAS失败会一直进行尝试。如果CAS长时间一直不成功可能会给CPU带来很大的开销。
只能保证一个共享变量的原子操作
当对一个共享变量执行操作时我们可以使用循环CAS的方式来保证原子操作但是对多个共享变量操作时循环CAS就无法保证操作的原子性这个时候就可以用锁来保证原子性。
引出来ABA问题
16_ABA问题
ABA问题怎么产生的
CAS会导致“ABA问题”。
CAS算法实现一个重要前提需要取出内存中某时刻的数据并在当下时刻比较并替换那么在这个时间差类会导致数据的变化。
比如说一个线程one从内存位置V中取出A这时候另一个线程two也从内存中取出A并且线程two进行了一些操作将值变成了B,然后线程two又将V位置的数据变成A这时候线程one进行CAS操作发现内存中仍然是A然后线程one操作成功。
尽管线程one的CAS操作成功但是不代表这个过程就是没有问题的。
17_AtomicReference原子引用
import java.util.concurrent.atomic.AtomicReference;class User{String userName;int age;public User(String userName, int age) {this.userName userName;this.age age;}Overridepublic String toString() {return String.format(User [userName%s, age%s], userName, age);}}public class AtomicReferenceDemo {public static void main(String[] args){User z3 new User( z3,22);User li4 new User(li4 ,25);AtomicReferenceUser atomicReference new AtomicReference();atomicReference.set(z3);System.out.println(atomicReference.compareAndSet(z3, li4)\tatomicReference.get().toString());System.out.println(atomicReference.compareAndSet(z3, li4)\tatomicReference.get().toString());}
}
输出结果
true User [userNameli4, age25]
false User [userNameli4, age25]18_AtomicStampedReference版本号原子引用
原子引用 新增一种机制那就是修改版本号类似时间戳它用来解决ABA问题。
19_ABA问题的解决
ABA问题程序演示及解决方法演示
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicStampedReference;public class ABADemo {/*** 普通的原子引用包装类*/static AtomicReferenceInteger atomicReference new AtomicReference(100);// 传递两个值一个是初始值一个是初始版本号static AtomicStampedReferenceInteger atomicStampedReference new AtomicStampedReference(100, 1);public static void main(String[] args) {System.out.println(以下是ABA问题的产生);new Thread(() - {// 把100 改成 101 然后在改成100也就是ABAatomicReference.compareAndSet(100, 101);atomicReference.compareAndSet(101, 100);}, t1).start();new Thread(() - {try {// 睡眠一秒保证t1线程完成了ABA操作TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}// 把100 改成 101 然后在改成100也就是ABASystem.out.println(atomicReference.compareAndSet(100, 2019) \t atomicReference.get());}, t2).start();/try {TimeUnit.SECONDS.sleep(2);} catch (Exception e) {e.printStackTrace();}/System.out.println(以下是ABA问题的解决);new Thread(() - {// 获取版本号int stamp atomicStampedReference.getStamp();System.out.println(Thread.currentThread().getName() \t 第一次版本号 stamp);// 暂停t3一秒钟try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}// 传入4个值期望值更新值期望版本号更新版本号atomicStampedReference.compareAndSet(100, 101, atomicStampedReference.getStamp(),atomicStampedReference.getStamp() 1);System.out.println(Thread.currentThread().getName() \t 第二次版本号 atomicStampedReference.getStamp());atomicStampedReference.compareAndSet(101, 100, atomicStampedReference.getStamp(),atomicStampedReference.getStamp() 1);System.out.println(Thread.currentThread().getName() \t 第三次版本号 atomicStampedReference.getStamp());}, t3).start();new Thread(() - {// 获取版本号int stamp atomicStampedReference.getStamp();System.out.println(Thread.currentThread().getName() \t 第一次版本号 stamp);// 暂停t4 3秒钟保证t3线程也进行一次ABA问题try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace();}boolean result atomicStampedReference.compareAndSet(100, 2019, stamp, stamp 1);System.out.println(Thread.currentThread().getName() \t 修改成功否 result \t 当前最新实际版本号 atomicStampedReference.getStamp());System.out.println(Thread.currentThread().getName() \t 当前实际最新值 atomicStampedReference.getReference());}, t4).start();}
}输出结果
以下是ABA问题的产生
true 2019
以下是ABA问题的解决
t3 第一次版本号1
t4 第一次版本号1
t3 第二次版本号2
t3 第三次版本号3
t4 修改成功否false 当前最新实际版本号3
t4 当前实际最新值10020_集合类不安全之并发修改异常
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import java.util.Vector;public class ArrayListNotSafeDemo {public static void main(String[] args) {ListString list new ArrayList();//ListString list new Vector();//ListString list Collections.synchronizedList(new ArrayList());for (int i 0; i 30; i) {new Thread(() - {list.add(UUID.randomUUID().toString().substring(0, 8));System.out.println(list);}, String.valueOf(i)).start();}}
}上述程序会抛java.util.ConcurrentModificationException
解决方法之一Vector
解决方法之二Collections.synchronizedList()
21_集合类不安全之写时复制
上一节程序导致抛java.util.ConcurrentModificationException的原因解析
先观察下抛错打印栈堆信息
java.util.ConcurrentModificationExceptionat java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)at java.util.ArrayList$Itr.next(ArrayList.java:859)at java.util.AbstractCollection.toString(AbstractCollection.java:461)at java.lang.String.valueOf(String.java:2994)at java.io.PrintStream.println(PrintStream.java:821)at com.lun.collection.ArrayListNotSafeDemo.lambda$0(ArrayListNotSafeDemo.java:20)at java.lang.Thread.run(Thread.java:748)可看出toString()Itr.next()Itr.checkForComodification()后抛出异常那么看看它们next()checkForComodification()源码
public class ArrayListE extends AbstractListEimplements ListE, RandomAccess, Cloneable, java.io.Serializable{...private class Itr implements IteratorE {int cursor; // index of next element to returnint lastRet -1; // index of last element returned; -1 if no suchint expectedModCount modCount;//modCount在AbstractList类声明Itr() {}...SuppressWarnings(unchecked)public E next() {checkForComodification();...}final void checkForComodification() {if (modCount ! expectedModCount)throw new ConcurrentModificationException();//---异常在此抛出}}public boolean add(E e) {ensureCapacityInternal(size 1); // Increments modCount!!elementData[size] e;return true;}private void ensureCapacityInternal(int minCapacity) {ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));}private void ensureExplicitCapacity(int minCapacity) {modCount;//添加时修改了modCount的值// overflow-conscious codeif (minCapacity - elementData.length 0)grow(minCapacity);}...
}public abstract class AbstractListE extends AbstractCollectionE implements ListE {...protected transient int modCount 0;...
}modCount具体详细说明如下 The number of times this list has been structurally modified. Structural modifications are those that change the size of the list, or otherwise perturb it in such a fashion that iterations in progress may yield incorrect results. This field is used by the iterator and list iterator implementation returned by the iterator and listIterator methods. If the value of this field changes unexpectedly, the iterator (or list iterator) will throw a ConcurrentModificationException in response to the next, remove, previous, set or add operations. This provides fail-fast behavior, rather than non-deterministic behavior in the face of concurrent modification during iteration. link 综上所述假设线程A将通过迭代器next()获取下一元素时从而将其打印出来。但之前其他某线程添加新元素至list结构发生了改变modCount自增。当线程A运行到checkForComodification()expectedModCount是modCount之前自增的值判定modCount ! expectedModCount为真继而抛出ConcurrentModificationException。
解决方法之三CopyOnWriteArrayList推荐
public class CopyOnWriteArrayListEimplements ListE, RandomAccess, Cloneable, java.io.Serializable {/** The array, accessed only via getArray/setArray. */private transient volatile Object[] array;final Object[] getArray() {return array;}final void setArray(Object[] a) {array a;}...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();}}...public String toString() {return Arrays.toString(getArray());}...
}CopyOnWrite容器即写时复制的容器。待一个容器添加元素的时候不直接往当前容器Object[]添加而是先将当前容器Object[]进行copy复制出一个新的容器Object[] newELements然后新的容器Object[ ] newELements里添加元素添加完元素之后再将原容器的引用指向新的容器setArray (newELements)。
这样做的好处是可以对CopyOnWrite容器进行并发的读而不需要加锁区别于Vector和Collections.synchronizedList()因为当前容器不会添加任何元素。所以CopyOnWrite容器也是一种读写分离的思想读和写不同的容器。
22_集合类不安全之Set
HashSet也是非线性安全的。HashSet内部是包装了一个HashMap的
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArraySet;public class SetNotSafeDemo {public static void main(String[] args) {SetString set new HashSet();//SetString set Collections.synchronizedSet(new HashSet());//SetString set new CopyOnWriteArraySetString();for (int i 0; i 30; i) {new Thread(() - {set.add(UUID.randomUUID().toString().substring(0, 8));System.out.println(set);}, String.valueOf(i)).start();}}
}解决方法
Collections.synchronizedSet(new HashSet())CopyOnWriteArraySet()推荐 CopyOnWriteArraySet源码一览
public class CopyOnWriteArraySetE extends AbstractSetEimplements java.io.Serializable {private static final long serialVersionUID 5457747651344034263L;private final CopyOnWriteArrayListE al;/*** Creates an empty set.*/public CopyOnWriteArraySet() {al new CopyOnWriteArrayListE();}public CopyOnWriteArraySet(Collection? extends E c) {if (c.getClass() CopyOnWriteArraySet.class) {SuppressWarnings(unchecked) CopyOnWriteArraySetE cc (CopyOnWriteArraySetE)c;al new CopyOnWriteArrayListE(cc.al);}else {al new CopyOnWriteArrayListE();al.addAllAbsent(c);}}//可看出CopyOnWriteArraySet包装了一个CopyOnWriteArrayList...public boolean add(E e) {return al.addIfAbsent(e);}public boolean addIfAbsent(E e) {Object[] snapshot getArray();return indexOf(e, snapshot, 0, snapshot.length) 0 ? false :addIfAbsent(e, snapshot);}//暴力查找private static int indexOf(Object o, Object[] elements,int index, int fence) {if (o null) {for (int i index; i fence; i)if (elements[i] null)return i;} else {for (int i index; i fence; i)if (o.equals(elements[i]))return i;}return -1;}private boolean addIfAbsent(E e, Object[] snapshot) {final ReentrantLock lock this.lock;lock.lock();try {Object[] current getArray();int len current.length;if (snapshot ! current) {//还要检查多一次元素存在性生怕别的线程已经插入了// Optimize for lost race to another addXXX operationint common Math.min(snapshot.length, len);for (int i 0; i common; i)if (current[i] ! snapshot[i] eq(e, current[i]))return false;if (indexOf(e, current, common, len) 0)return false;}Object[] newElements Arrays.copyOf(current, len 1);newElements[len] e;setArray(newElements);return true;} finally {lock.unlock();}}...}23_集合类不安全之Map
import java.util.Collections;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;public class MapNotSafeDemo {public static void main(String[] args) {MapString, String map new HashMap();
// MapString, String map Collections.synchronizedMap(new HashMap());
// MapString, String map new ConcurrentHashMap();
// MapString, String map new Hashtable();for (int i 0; i 30; i) {new Thread(() - {map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0, 8));System.out.println(map);}, String.valueOf(i)).start();}}}解决方法
HashTableCollections.synchronizedMap(new HashMap())ConcurrencyMap()推荐
24_TransferValue醒脑小练习
Java的参数传递是值传递不是引用传递。
下面程序体验下上一句的含义
class Person {private Integer id;private String personName;public Person(String personName) {this.personName personName;}public Integer getId() {return id;}public void setId(Integer id) {this.id id;}public String getPersonName() {return personName;}public void setPersonName(String personName) {this.personName personName;}
}public class TransferValueDemo {public void changeValue1(int age) {age 30;}public void changeValue2(Person person) {person.setPersonName(XXXX);}public void changeValue3(String str) {str XXX;}public static void main(String[] args) {TransferValueDemo test new TransferValueDemo();// 定义基本数据类型int age 20;test.changeValue1(age);System.out.println(age ---- age);// 实例化person类Person person new Person(abc);test.changeValue2(person);System.out.println(personName----- person.getPersonName());// StringString str abc;test.changeValue3(str);System.out.println(string----- str);}
}输出结果
age ----20
personName-----XXXX
string-----abc25_java锁之公平和非公平锁
是什么 公平锁―是指多个线程按照申请锁的顺序来获取锁类似排队打饭先来后到。 非公平锁是指多个线程获取锁的顺序并不是按照申请锁的顺序有可能后中请的线程比先中请的线程优先获取锁。在高并发的情况下有可能会造成优先级反转或者饥饿现象
并发包中ReentrantLock的创建可以指定构造函数的boolean类型来得到公平锁或非公平锁默认是非公平锁。 The constructor for this class accepts an optional fairness parameter. When set true, under contention, locks favor granting access to the longest-waiting thread. Otherwise this lock does not guarantee any particular access order. Programs using fair locks accessed by many threads may display lower overall throughput (i.e., are slower; often much slower) than those using the default setting, but have smaller variances in times to obtain locks and guarantee lack of starvation. Note however, that fairness of locks does not guarantee fairness of thread scheduling. Thus, one of many threads using a fair lock may obtain it multiple times in succession while other active threads are not progressing and not currently holding the lock. Also note that the untimed tryLock() method does not honor the fairness setting. It will succeed if the lock is available even if other threads are waiting. 此类的构造函数接受可选的公平性参数。当设置为true时在争用下锁有利于向等待时间最长的线程授予访问权限。否则此锁不保证任何特定的访问顺序。与使用默认设置的程序相比使用由许多线程访问的公平锁的程序可能显示出较低的总体吞吐量即较慢通常要慢得多但是在获得锁和保证没有饥饿的时间上差异较小。 但是请注意锁的公平性并不能保证线程调度的公平性。因此使用公平锁的多个线程中的一个线程可以连续多次获得公平锁而其他活动线程则没有进行并且当前没有持有该锁。还要注意不计时的 tryLock()方法不支持公平性设置。如果锁可用即使其他线程正在等待它也会成功。 link reentrant 英 [riːˈɛntrənt] 美 [ˌriˈɛntrənt] a. 可重入;可重入的;重入;可再入的;重进入 两者区别
关于两者区别
公平锁 Threads acquire a fair lock in the order in which they requested it.公平锁就是很公平在并发环境中每个线程在获取锁时会先查看此锁维护的等待队列如果为空或者当前线程是等待队列的第一个就占有锁否则就会加入到等待队列中以后会按照FIFO的规则从队列中取到自己。 非公平锁 a nonfair lock permits barging: threads requesting a lock can jump ahead of the queue of waiting threads if the lockhappens to be available when it is requested.非公平锁比较粗鲁上来就直接尝试占有锁如果尝试失败就再采用类似公平锁那种方式。
题外话
Java ReentrantLock而言通过构造函数指定该锁是否是公平锁默认是非公平锁。
非公平锁的优点在于吞吐量比公平锁大。
对于Synchronized而言也是一种非公平锁
26_java锁之可重入锁和递归锁理论知识
可重入锁也叫做递归锁
指的是同一线程外层函数获得锁之后内层递归函数仍然能获取该锁的代码在同一个线程在外层方法获取锁的时候在进入内层方法会自动获取锁。
也即是说线程可以进入任何一个它已经拥有的锁所同步着的代码块。
ReentrantLock/synchronized就是一个典型的可重入锁。
可重入锁最大的作用是避免死锁。
27_java锁之可重入锁和递归锁代码验证
Synchronized可入锁演示程序
class Phone {public synchronized void sendSMS() throws Exception{System.out.println(Thread.currentThread().getName() \t invoked sendSMS());// 在同步方法中调用另外一个同步方法sendEmail();}public synchronized void sendEmail() throws Exception{System.out.println(Thread.currentThread().getId() \t invoked sendEmail());}
}public class SynchronizedReentrantLockDemo {public static void main(String[] args) {Phone phone new Phone();// 两个线程操作资源列new Thread(() - {try {phone.sendSMS();} catch (Exception e) {e.printStackTrace();}}, t1).start();new Thread(() - {try {phone.sendSMS();} catch (Exception e) {e.printStackTrace();}}, t2).start();}}
输出结果
t1 invoked sendSMS()
11 invoked sendEmail()
t2 invoked sendSMS()
12 invoked sendEmail()ReentrantLock可重入锁演示程序
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;class Phone2 implements Runnable{Lock lock new ReentrantLock();/*** set进去的时候就加锁调用set方法的时候能否访问另外一个加锁的set方法*/public void getLock() {lock.lock();try {System.out.println(Thread.currentThread().getName() \t get Lock);setLock();} finally {lock.unlock();}}public void setLock() {lock.lock();try {System.out.println(Thread.currentThread().getName() \t set Lock);} finally {lock.unlock();}}Overridepublic void run() {getLock();}
}public class ReentrantLockDemo {public static void main(String[] args) {Phone2 phone new Phone2();/*** 因为Phone实现了Runnable接口*/Thread t3 new Thread(phone, t3);Thread t4 new Thread(phone, t4);t3.start();t4.start();}
}输出结果
t3 get Lock
t3 set Lock
t4 get Lock
t4 set Lock28_java锁之自旋锁理论知识
自旋锁Spin Lock
是指尝试获取锁的线程不会立即阻塞而是采用循环的方式去尝试获取锁这样的好处是减少线程上下文切换的消耗缺点是循环会消耗CPU 提到了互斥同步对性能最大的影响阻塞的实现挂起线程和恢复线程的操作都需要转入内核态完成这些操作给系统的并发性能带来了很大的压力。同时虚拟机的开发团队也注意到在许多应用上共享数据的锁定状态只会持续很短的一段时间为了这段时间去挂起和恢复线程并不值得。如果物理机器有一个以上的处理器能让两个或以上的线程同时并行执行我们就可以让后面请求锁的那个线程 “稍等一下”但不放弃处理器的执行时间看看持有锁的线程是否很快就会释放锁。为了让线程等待我们只需让线程执行一个忙循环自旋这项技术就是所谓的自旋锁。 《深入理解JVM.2nd》Page 398 29_java锁之自旋锁代码验证
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;public class SpinLockDemo {// 现在的泛型装的是Thread原子引用线程AtomicReferenceThread atomicReference new AtomicReference();public void myLock() {// 获取当前进来的线程Thread thread Thread.currentThread();System.out.println(Thread.currentThread().getName() \t come in );// 开始自旋期望值是null更新值是当前线程如果是null则更新为当前线程否者自旋while(!atomicReference.compareAndSet(null, thread)) {//摸鱼}}public void myUnLock() {// 获取当前进来的线程Thread thread Thread.currentThread();// 自己用完了后把atomicReference变成nullatomicReference.compareAndSet(thread, null);System.out.println(Thread.currentThread().getName() \t invoked myUnlock());}public static void main(String[] args) {SpinLockDemo spinLockDemo new SpinLockDemo();// 启动t1线程开始操作new Thread(() - {// 开始占有锁spinLockDemo.myLock();try {TimeUnit.SECONDS.sleep(5);} catch (InterruptedException e) {e.printStackTrace();}// 开始释放锁spinLockDemo.myUnLock();}, t1).start();// 让main线程暂停1秒使得t1线程先执行try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}// 1秒后启动t2线程开始占用这个锁new Thread(() - {// 开始占有锁spinLockDemo.myLock();// 开始释放锁spinLockDemo.myUnLock();}, t2).start();}
}输出结果
t1 come in
t2 come in
t1 invoked myUnlock()
t2 invoked myUnlock()30_java锁之读写锁理论知识
独占锁指该锁一次只能被一个线程所持有。对ReentrantLock和Synchronized而言都是独占锁
共享锁指该锁可被多个线程所持有。
多个线程同时读一个资源类没有任何问题所以为了满足并发量读取共享资源应该可以同时进行。但是如果有一个线程想去写共享资源来就不应该再有其它线程可以对该资源进行读或写。
对ReentrantReadWriteLock其读锁是共享锁其写锁是独占锁。
读锁的共享锁可保证并发读是非常高效的读写写读写写的过程是互斥的。
31_java锁之读写锁代码验证
实现一个读写缓存的操作假设开始没有加锁的时候会出现什么情况
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;class MyCache {private volatile MapString, Object map new HashMap();public void put(String key, Object value) {System.out.println(Thread.currentThread().getName() \t 正在写入 key);try {// 模拟网络拥堵延迟0.3秒TimeUnit.MILLISECONDS.sleep(300);} catch (InterruptedException e) {e.printStackTrace();}map.put(key, value);System.out.println(Thread.currentThread().getName() \t 写入完成);}public void get(String key) {System.out.println(Thread.currentThread().getName() \t 正在读取:);try {// 模拟网络拥堵延迟0.3秒TimeUnit.MILLISECONDS.sleep(300);} catch (InterruptedException e) {e.printStackTrace();}Object value map.get(key);System.out.println(Thread.currentThread().getName() \t 读取完成 value);}
}public class ReadWriteWithoutLockDemo {public static void main(String[] args) {MyCache myCache new MyCache();// 线程操作资源类5个线程写for (int i 0; i 5; i) {final int tempInt i;new Thread(() - {myCache.put(tempInt , tempInt );}, String.valueOf(i)).start();}// 线程操作资源类 5个线程读for (int i 0; i 5; i) {final int tempInt i;new Thread(() - {myCache.get(tempInt );}, String.valueOf(i)).start();}}}输出结果
0 正在写入0
1 正在写入1
3 正在写入3
2 正在写入2
4 正在写入4
0 正在读取:
1 正在读取:
2 正在读取:
4 正在读取:
3 正在读取:
1 写入完成
4 写入完成
0 写入完成
2 写入完成
3 写入完成
3 读取完成3
0 读取完成0
2 读取完成2
1 读取完成null
4 读取完成null看到有些线程读取到null可用ReentrantReadWriteLock解决
package com.lun.concurrency;import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantReadWriteLock;class MyCache2 {private volatile MapString, Object map new HashMap();private ReentrantReadWriteLock rwLock new ReentrantReadWriteLock();public void put(String key, Object value) {// 创建一个写锁rwLock.writeLock().lock();try {System.out.println(Thread.currentThread().getName() \t 正在写入 key);try {// 模拟网络拥堵延迟0.3秒TimeUnit.MILLISECONDS.sleep(300);} catch (InterruptedException e) {e.printStackTrace();}map.put(key, value);System.out.println(Thread.currentThread().getName() \t 写入完成);} catch (Exception e) {e.printStackTrace();} finally {// 写锁 释放rwLock.writeLock().unlock();}}public void get(String key) {// 读锁rwLock.readLock().lock();try {System.out.println(Thread.currentThread().getName() \t 正在读取:);try {// 模拟网络拥堵延迟0.3秒TimeUnit.MILLISECONDS.sleep(300);} catch (InterruptedException e) {e.printStackTrace();}Object value map.get(key);System.out.println(Thread.currentThread().getName() \t 读取完成 value);} catch (Exception e) {e.printStackTrace();} finally {// 读锁释放rwLock.readLock().unlock();}}public void clean() {map.clear();}}public class ReadWriteWithLockDemo {public static void main(String[] args) {MyCache2 myCache new MyCache2();// 线程操作资源类5个线程写for (int i 1; i 5; i) {// lambda表达式内部必须是finalfinal int tempInt i;new Thread(() - {myCache.put(tempInt , tempInt );}, String.valueOf(i)).start();}// 线程操作资源类 5个线程读for (int i 1; i 5; i) {// lambda表达式内部必须是finalfinal int tempInt i;new Thread(() - {myCache.get(tempInt );}, String.valueOf(i)).start();}}
}
输出结果
1 正在写入1
1 写入完成
2 正在写入2
2 写入完成
3 正在写入3
3 写入完成
5 正在写入5
5 写入完成
4 正在写入4
4 写入完成
2 正在读取:
3 正在读取:
1 正在读取:
5 正在读取:
4 正在读取:
3 读取完成3
2 读取完成2
1 读取完成1
5 读取完成5
4 读取完成432_CountDownLatch
让一线程阻塞直到另一些线程完成一系列操作才被唤醒。
CountDownLatch主要有两个方法await()countDown()。
当一个或多个线程调用await()时调用线程会被阻塞。其它线程调用countDown()会将计数器减1(调用countDown方法的线程不会阻塞)当计数器的值变为零时因调用await方法被阻塞的线程会被唤醒继续执行。 latch 英 [lætʃ] 美 [lætʃ] n. 门闩;插销;碰锁;弹簧锁 v. 用插销插上;用碰锁锁上 假设一个自习室里有7个人其中有一个是班长班长的主要职责就是在其它6个同学走了后关灯锁教室门然后走人因此班长是需要最后一个走的那么有什么方法能够控制班长这个线程是最后一个执行而其它线程是随机执行的
import java.util.concurrent.CountDownLatch;public class CountDownLatchDemo {public static void main(String[] args) throws InterruptedException {// 计数器CountDownLatch countDownLatch new CountDownLatch(6);for (int i 0; 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() \t 班长最后关门);}
}
输出结果
0 上完自习离开教室
6 上完自习离开教室
4 上完自习离开教室
5 上完自习离开教室
3 上完自习离开教室
1 上完自习离开教室
2 上完自习离开教室
main 班长最后关门温习枚举
枚举 CountDownLatch
程序演示秦国统一六国
import java.util.Objects;public enum CountryEnum {ONE(1, 齐), TWO(2, 楚), THREE(3, 燕), FOUR(4, 赵), FIVE(5, 魏), SIX(6, 韩);private Integer retcode;private String retMessage;CountryEnum(Integer retcode, String retMessage) {this.retcode retcode;this.retMessage retMessage;}public static CountryEnum forEach_countryEnum(int index) {CountryEnum[] myArray CountryEnum.values();for(CountryEnum ce : myArray) {if(Objects.equals(index, ce.getRetcode())) {return ce;}}return null;}public Integer getRetcode() {return retcode;}public void setRetcode(Integer retcode) {this.retcode retcode;}public String getRetMessage() {return retMessage;}public void setRetMessage(String retMessage) {this.retMessage retMessage;}}import java.util.concurrent.CountDownLatch;public class UnifySixCountriesDemo {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() 秦国统一中原。);}
}
输出结果
齐国被灭了
燕国被灭了
楚国被灭了
魏国被灭了
韩国被灭了
赵国被灭了
main 秦国统一中原。33_CyclicBarrierDemo
CyclicBarrier的字面意思就是可循环Cyclic使用的屏障Barrier。它要求做的事情是让一组线程到达一个屏障也可以叫同步点时被阻塞直到最后一个线程到达屏障时屏障才会开门所有被屏障拦截的线程才会继续干活线程进入屏障通过CyclicBarrier的await方法。
CyclicBarrier与CountDownLatch的区别CyclicBarrier可重复多次而CountDownLatch只能是一次。
程序演示集齐7个龙珠召唤神龙
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;public class SummonTheDragonDemo {public static void main(String[] args) {/*** 定义一个循环屏障参数1需要累加的值参数2 需要执行的方法*/CyclicBarrier cyclicBarrier new CyclicBarrier(7, () - {System.out.println(召唤神龙);});for (int i 1; i 7; i) {final Integer tempInt i;new Thread(() - {System.out.println(Thread.currentThread().getName() \t 收集到 第 tempInt 颗龙珠);try {// 先到的被阻塞等全部线程完成后才能执行方法cyclicBarrier.await();} catch (InterruptedException e) {e.printStackTrace();} catch (BrokenBarrierException e) {e.printStackTrace();}}, String.valueOf(i)).start();}}
}输出结果
2 收集到 第2颗龙珠
6 收集到 第6颗龙珠
1 收集到 第1颗龙珠
7 收集到 第7颗龙珠
5 收集到 第5颗龙珠
4 收集到 第4颗龙珠
3 收集到 第3颗龙珠
召唤神龙来自《Java编程思想》的例子展现CyclicBarrier的可循环性
import java.util.concurrent.*;
import java.util.*;class Horse implements Runnable {private static int counter 0;private final int id counter;private int strides 0;private static Random rand new Random(47);private static CyclicBarrier barrier;public Horse(CyclicBarrier b) {barrier b;}public synchronized int getStrides() {return strides;}public void run() {try {while (!Thread.interrupted()) {//没有中断就不断循环synchronized (this) {//模拟马单位时间的移动距离strides rand.nextInt(3); // Produces 0, 1 or 2}barrier.await();//---等待其他马到齐到循环屏障}} catch (InterruptedException e) {// A legitimate way to exit} catch (BrokenBarrierException e) {// This one we want to know aboutthrow new RuntimeException(e);}}public String toString() {return Horse id ;}public String tracks() {StringBuilder s new StringBuilder();for (int i 0; i getStrides(); i)s.append(*);s.append(id);return s.toString();}
}public class HorseRace {static final int FINISH_LINE 75;private ListHorse horses new ArrayListHorse();private ExecutorService exec Executors.newCachedThreadPool();private CyclicBarrier barrier;public HorseRace(int nHorses, final int pause) {//初始化循环屏障barrier new CyclicBarrier(nHorses, new Runnable() {// 循环多次执行的任务public void run() {// The fence on the racetrackStringBuilder s new StringBuilder();for (int i 0; i FINISH_LINE; i)s.append(); System.out.println(s);//打印马移动距离for (Horse horse : horses)System.out.println(horse.tracks());//判断有没有马到终点了for (Horse horse : horses)if (horse.getStrides() FINISH_LINE) {System.out.println(horse won!);exec.shutdownNow();// 有只马跑赢了所有任务都结束了return;}try {TimeUnit.MILLISECONDS.sleep(pause);} catch (InterruptedException e) {System.out.println(barrier-action sleep interrupted);}}});// 开跑for (int i 0; i nHorses; i) {Horse horse new Horse(barrier);horses.add(horse);exec.execute(horse);}}public static void main(String[] args) {int nHorses 7;int pause 200;new HorseRace(nHorses, pause);}
}输出结果
...省略一些...**********************************************************0
************************************************************1
******************************************************2
***********************************************************************3
*************************************************************************4
*****************************************************************5
*****************************************************************6**********************************************************0
************************************************************1
*******************************************************2
***********************************************************************3
**************************************************************************4
*****************************************************************5
*******************************************************************6***********************************************************0
*************************************************************1
*******************************************************2
***********************************************************************3
****************************************************************************4
*******************************************************************5
********************************************************************6
Horse 4 won!34_SemaphoreDemo
信号量主要用于两个目的一个是用于多个共享资源的互斥使用另一个用于并发线程数的控制。 semaphore 英 [ˈseməfɔː®] 美 [ˈseməfɔːr] n. 信号标;旗语 v. 打旗语;(用其他类似的信号系统)发信号 正常的锁(concurrency.locks或synchronized锁)在任何时刻都只允许一个任务访问一项资源而 Semaphore允许n个任务同时访问这个资源。
模拟一个抢车位的场景假设一共有6个车3个停车位
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;public class SemaphoreDemo {public static void main(String[] args) {/*** 初始化一个信号量为3默认是false 非公平锁 模拟3个停车位*/Semaphore semaphore new Semaphore(3, false);// 模拟6部车for (int i 0; i 6; i) {new Thread(() - {try {// 代表一辆车已经占用了该车位semaphore.acquire(); // 抢占System.out.println(Thread.currentThread().getName() \t 抢到车位);// 每个车停3秒try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() \t 离开车位);} catch (InterruptedException e) {e.printStackTrace();} finally {// 释放停车位semaphore.release();}}, String.valueOf(i)).start();}}
}输出结果
1 抢到车位
2 抢到车位
0 抢到车位
0 离开车位
2 离开车位
1 离开车位
5 抢到车位
4 抢到车位
3 抢到车位
5 离开车位
4 离开车位
3 离开车位35_阻塞队列理论 阻塞队列有没有好的一面 不得不阻塞你如何管理
36_阻塞队列接口结构和实现类
阻塞队列顾名思义首先它是一个队列而一个阻塞队列在数据结构中所起的作用大致如下图所示 线程1往阻塞队列中添加元素而线程2从阻塞队列中移除元素。
当阻塞队列是空时从队列中获取元素的操作将会被阻塞。
当阻塞队列是满时往队列里添加元素的操作将会被阻塞。
试图从空的阻塞队列中获取元素的线程将会被阻塞直到其他的线程往空的队列插入新的元素。
同样试图往已满的阻塞队列中添加新元素的线程同样也会被阻塞直到其他的线程从列中移除一个或者多个元素或者完全清空队列后使队列重新变得空闲起来并后续新增
为什么用有什么好处
在多线程领域所谓阻塞在某些情况下余挂起线程即阻塞一旦条件满足被挂起的线程又会自动被唤醒
为什么需要BlockingQueue 好处是我们不需要关心什么时候需要阻塞线程什么时候需要唤醒线程因为这一切BlockingQueue都给你一手包办了
在Concurrent包发布以前在多线程环境下我们每个程序员都必须去自己控制这些细节尤其还要兼顾效率和线程安全而这会给我们的程序带来不小的复杂度。
架构介绍 种类分析
ArrayBlockingQueue由数组结构组成的有界阻塞队列。LinkedBlockingQueue由链表结构组成的有界但大小默认值为Integer.MAX_VALUE阻塞队列。PriorityBlockingQueue支持优先级排序的无界阻塞队列。DelayQueue使用优先级队列实现妁延迟无界阻塞队列。SynchronousQueue不存储元素的阻塞队列。LinkedTransferQueue由链表结构绒成的无界阻塞队列。LinkedBlockingDeque由链表结构组成的双向阻塞队列。
BlockingQueue的核心方法
方法类型抛出异常特殊值阻塞超时插入add(e)offer(e)put(e)offer(e,time,unit)移除remove()poll()take()poll(time,unit)检查element()peek()不可用不可用
性质说明抛出异常当阻塞队列满时在往队列中add插入元素会抛出 IIIegalStateExceptionQueue full当阻塞队列空时再往队列中remove移除元素会抛出NoSuchException特殊性插入方法成功true失败false 移除方法成功返回出队列元素队列没有就返回空一直阻塞当阻塞队列满时生产者继续往队列里put元素队列会一直阻塞生产线程直到put数据or响应中断退出。当阻塞队列空时消费者线程试图从队列里take元素队列会一直阻塞消费者线程直到队列可用。超时退出当阻塞队列满时队里会阻塞生产者线程一定时间超过限时后生产者线程会退出
37_阻塞队列api之抛出异常组
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;public class BlockingQueueExceptionDemo {public static void main(String[] args) {BlockingQueueString blockingQueue new ArrayBlockingQueue(3);System.out.println(blockingQueue.add(a));System.out.println(blockingQueue.add(b));System.out.println(blockingQueue.add(c));try {//抛出 java.lang.IllegalStateException: Queue fullSystem.out.println(blockingQueue.add(XXX));} catch (Exception e) {System.err.println(e);}System.out.println(blockingQueue.element());///System.out.println(blockingQueue.remove());System.out.println(blockingQueue.remove());System.out.println(blockingQueue.remove());try {//抛出 java.util.NoSuchElementExceptionSystem.out.println(blockingQueue.remove()); } catch (Exception e) {System.err.println(e);}try {//element()相当于peek(),但element()会抛NoSuchElementExceptionSystem.out.println(blockingQueue.element());} catch (Exception e) {System.err.println(e);}}
}输出结果
true
true
true
a
java.lang.IllegalStateException: Queue full
a
b
c
java.util.NoSuchElementException
java.util.NoSuchElementException38_阻塞队列api之返回布尔值组
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;public class BlockingQueueBooleanDemo {public static void main(String[] args) {BlockingQueueString blockingQueue new ArrayBlockingQueue(3);System.out.println(blockingQueue.offer(a));System.out.println(blockingQueue.offer(b));System.out.println(blockingQueue.offer(c));System.out.println(blockingQueue.offer(d));System.out.println(blockingQueue.poll());System.out.println(blockingQueue.poll());System.out.println(blockingQueue.poll());System.out.println(blockingQueue.poll());}
}输出结果
true
true
true
false
a
b
c
null39_阻塞队列api之阻塞和超时控制
队列阻塞演示
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;public class BlockingQueueBlockedDemo {public static void main(String[] args) throws InterruptedException {BlockingQueueString blockingQueue new ArrayBlockingQueue(3);new Thread(()-{try {blockingQueue.put(a);blockingQueue.put(b);blockingQueue.put(c);blockingQueue.put(c);//将会阻塞,直到主线程take()System.out.println(it was blocked.);} catch (InterruptedException e) {e.printStackTrace();}}).start();TimeUnit.SECONDS.sleep(2);try {blockingQueue.take();blockingQueue.take();blockingQueue.take();blockingQueue.take();System.out.println(Blocking...);blockingQueue.take();//将会阻塞} catch (InterruptedException e) {e.printStackTrace();}}}阻塞超时放弃演示
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;public class BlockingQueueTimeoutDemo {public static void main(String[] args) throws InterruptedException {BlockingQueueString blockingQueue new ArrayBlockingQueue(3);System.out.println(Offer.);System.out.println(blockingQueue.offer(a, 2L, TimeUnit.SECONDS));System.out.println(blockingQueue.offer(b, 2L, TimeUnit.SECONDS));System.out.println(blockingQueue.offer(c, 2L, TimeUnit.SECONDS));System.out.println(blockingQueue.offer(d, 2L, TimeUnit.SECONDS));System.out.println(Poll.);System.out.println(blockingQueue.poll(2L, TimeUnit.SECONDS));System.out.println(blockingQueue.poll(2L, TimeUnit.SECONDS));System.out.println(blockingQueue.poll(2L, TimeUnit.SECONDS));System.out.println(blockingQueue.poll(2L, TimeUnit.SECONDS));}}输出结果
Offer.
true
true
true
false
Poll.
a
b
c
null40_阻塞队列之同步SynchronousQueue队列
SynchronousQueue没有容量。
与其他BlockingQueue不同SynchronousQueue是一个不存储元素的BlockingQueue。
每一个put操作必须要等待一个take操作否则不能继续添加元素反之亦然。 A blocking queue in which each insert operation must wait for a corresponding remove operation by another thread, and vice versa. A synchronous queue does not have any internal capacity, not even a capacity of one. You cannot peek at a synchronous queue because an element is only present when you try to remove it; You cannot insert an element (using any method) unless another thread is trying to remove it; You cannot iterate as there is nothing to iterate. The head of the queue is the element that the first queued inserting thread is trying to add to the queue; if there is no such queued thread then no element is available for removal and poll() will return null. For purposes of other Collection methods (for example contains), a SynchronousQueue acts as an empty collection. This queue does not permit null elements. link import java.util.concurrent.BlockingQueue;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;public class SynchronousQueueDemo {public static void main(String[] args) {BlockingQueueString blockingQueue new SynchronousQueue();new Thread(() - {try { System.out.println(Thread.currentThread().getName() \t put A );blockingQueue.put(A);System.out.println(Thread.currentThread().getName() \t put B );blockingQueue.put(B); System.out.println(Thread.currentThread().getName() \t put C );blockingQueue.put(C); } catch (InterruptedException e) {e.printStackTrace();}}, t1).start();new Thread(() - {try {try {TimeUnit.SECONDS.sleep(5);} catch (InterruptedException e) {e.printStackTrace();}blockingQueue.take();System.out.println(Thread.currentThread().getName() \t take A );try {TimeUnit.SECONDS.sleep(5);} catch (InterruptedException e) {e.printStackTrace();}blockingQueue.take();System.out.println(Thread.currentThread().getName() \t take B );try {TimeUnit.SECONDS.sleep(5);} catch (InterruptedException e) {e.printStackTrace();}blockingQueue.take();System.out.println(Thread.currentThread().getName() \t take C );} catch (InterruptedException e) {e.printStackTrace();}}, t2).start();}}41_线程通信之生产者消费者传统版
阻塞队列用在哪里
生产者消费者模式 传统版synchronized, wait, notify阻塞队列版lock, await, signal 线程池消息中间件
实现一个简单的生产者消费者模式
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();} catch (Exception e) {e.printStackTrace();} 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();} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}}
}public class TraditionalProducerConsumerDemo {public static void main(String[] args) {ShareData shareData new ShareData();// t1线程生产new Thread(() - {for (int i 0; i 5; i) {try {shareData.increment();} catch (Exception e) {e.printStackTrace();}}}, t1).start();// t2线程消费new Thread(() - {for (int i 0; i 5; i) {try {shareData.decrement();} catch (Exception e) {e.printStackTrace();}}}, t2).start();}
}输出结果
t1 1
t2 0
t1 1
t2 0
t1 1
t2 0
t1 1
t2 0
t1 1
t2 0
t1 1
t2 0
t1 1
t2 0
t1 1
t2 0
t1 1
t2 0
t1 1
t2 0注意increment()和decrement()内的
// 判断
while(number ! 0) {// 等待不能生产condition.await();
}不能用
// 判断
if(number ! 0) {// 等待不能生产condition.await();
}否则会出现虚假唤醒出现异常状况。
42_Synchronized和Lock有什么区别
synchronized属于JVM层面属于java的关键字 monitorenter底层是通过monitor对象来完成其实wait/notify等方法也依赖于monitor对象 只能在同步块或者方法中才能调用 wait/ notify等方法Lock是具体类java.util.concurrent.locks.Lock是api层面的锁 使用方法 synchronized不需要用户去手动释放锁当synchronized代码执行后系统会自动让线程释放对锁的占用。ReentrantLock则需要用户去手动释放锁若没有主动释放锁就有可能出现死锁的现象需要lock() 和 unlock() 配置try catch语句来完成 等待是否中断 synchronized不可中断除非抛出异常或者正常运行完成。ReentrantLock可中断可以设置超时方法 设置超时方法trylock(long timeout, TimeUnit unit)lockInterrupible() 放代码块中调用interrupt() 方法可以中断 加锁是否公平 synchronized非公平锁ReentrantLock默认非公平锁构造函数可以传递boolean值true为公平锁false为非公平锁 锁绑定多个条件Condition synchronized没有要么随机要么全部唤醒ReentrantLock用来实现分组唤醒需要唤醒的线程可以精确唤醒而不是像synchronized那样要么随机要么全部唤醒
43_锁绑定多个条件Condition
实现场景
多线程之间按顺序调用实现 A- B - C 三个线程启动要求如下 AA打印5次BB打印10次CC打印15次 紧接着 AA打印5次BB打印10次CC打印15次 … 来10轮
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;class ShareResource {// A 1 B 2 c 3private int number 1;// 创建一个重入锁private Lock lock new ReentrantLock();// 这三个相当于备用钥匙private Condition condition1 lock.newCondition();private Condition condition2 lock.newCondition();private Condition condition3 lock.newCondition();public void print5() {lock.lock();try {// 判断while(number ! 1) {// 不等于1需要等待condition1.await();}// 干活for (int i 0; i 5; i) {System.out.println(Thread.currentThread().getName() \t number \t i);}// 唤醒 干完活后需要通知B线程执行number 2;// 通知2号去干活了condition2.signal();} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}}public void print10() {lock.lock();try {// 判断while(number ! 2) {// 不等于1需要等待condition2.await();}// 干活for (int i 0; i 10; i) {System.out.println(Thread.currentThread().getName() \t number \t i);}// 唤醒 干完活后需要通知C线程执行number 3;// 通知2号去干活了condition3.signal();} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}}public void print15() {lock.lock();try {// 判断while(number ! 3) {// 不等于1需要等待condition3.await();}// 干活for (int i 0; i 15; i) {System.out.println(Thread.currentThread().getName() \t number \t i);}// 唤醒 干完活后需要通知C线程执行number 1;// 通知1号去干活了condition1.signal();} catch (Exception e) {e.printStackTrace();} finally {lock.unlock();}}
}public class SynchronizedAndReentrantLockDemo {public static void main(String[] args) {ShareResource shareResource new ShareResource();int num 10;new Thread(() - {for (int i 0; i num; i) {shareResource.print5();}}, A).start();new Thread(() - {for (int i 0; i num; i) {shareResource.print10();}}, B).start();new Thread(() - {for (int i 0; i num; i) {shareResource.print15();}}, C).start();}
}
输出结果
...
A 1 0
A 1 1
A 1 2
A 1 3
A 1 4
B 2 0
B 2 1
B 2 2
B 2 3
B 2 4
B 2 5
B 2 6
B 2 7
B 2 8
B 2 9
C 3 0
C 3 1
C 3 2
C 3 3
C 3 4
C 3 5
C 3 6
C 3 7
C 3 8
C 3 9
C 3 10
C 3 11
C 3 12
C 3 13
C 3 14
A 1 0
A 1 1
A 1 2
A 1 3
A 1 4
B 2 0
B 2 1
B 2 2
B 2 3
B 2 4
B 2 5
B 2 6
B 2 7
B 2 8
B 2 9
C 3 0
C 3 1
C 3 2
C 3 3
C 3 4
C 3 5
C 3 6
C 3 7
C 3 8
C 3 9
C 3 10
C 3 11
C 3 12
C 3 13
C 3 1444_线程通信之生产者消费者阻塞队列版
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;class MyResource {// 默认开启进行生产消费// 这里用到了volatile是为了保持数据的可见性也就是当TLAG修改时要马上通知其它线程进行修改private volatile boolean FLAG true;// 使用原子包装类而不用numberprivate AtomicInteger atomicInteger new AtomicInteger();// 这里不能为了满足条件而实例化一个具体的SynchronousBlockingQueueBlockingQueueString blockingQueue null;// 而应该采用依赖注入里面的构造注入方法传入public MyResource(BlockingQueueString blockingQueue) {this.blockingQueue blockingQueue;// 查询出传入的class是什么System.out.println(blockingQueue.getClass().getName());}public void myProducer() throws Exception{String data null;boolean retValue;// 多线程环境的判断一定要使用while进行防止出现虚假唤醒// 当FLAG为true的时候开始生产while(FLAG) {data atomicInteger.incrementAndGet() ;// 2秒存入1个dataretValue 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 失败 );}try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}}System.out.println(Thread.currentThread().getName() \t 停止生产表示FLAGfalse生产介绍);}public void myConsumer() throws Exception{String retValue;// 多线程环境的判断一定要使用while进行防止出现虚假唤醒// 当FLAG为true的时候开始生产while(FLAG) {// 2秒存入1个dataretValue blockingQueue.poll(2L, TimeUnit.SECONDS);if(retValue ! null retValue ! ) {System.out.println(Thread.currentThread().getName() \t 消费队列: retValue 成功 );} else {FLAG false;System.out.println(Thread.currentThread().getName() \t 消费失败队列中已为空退出 );// 退出消费队列return;}}}/*** 停止生产的判断*/public void stop() {this.FLAG false;}}
public class ProducerConsumerWithBlockingQueueDemo {public static void main(String[] args) {// 传入具体的实现类 ArrayBlockingQueueMyResource myResource new MyResource(new ArrayBlockingQueueString(10));new Thread(() - {System.out.println(Thread.currentThread().getName() \t 生产线程启动\n\n);try {myResource.myProducer();System.out.println(\n);} catch (Exception e) {e.printStackTrace();}}, producer).start();new Thread(() - {System.out.println(Thread.currentThread().getName() \t 消费线程启动);try {myResource.myConsumer();} catch (Exception e) {e.printStackTrace();}}, consumer).start();// 5秒后停止生产和消费try {TimeUnit.SECONDS.sleep(5);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(\n\n5秒中后生产和消费线程停止线程结束);myResource.stop();}
}输出结果
java.util.concurrent.ArrayBlockingQueue
producer 生产线程启动consumer 消费线程启动
producer 插入队列:1成功
consumer 消费队列:1成功
producer 插入队列:2成功
consumer 消费队列:2成功
producer 插入队列:3成功
consumer 消费队列:3成功
producer 插入队列:4成功
consumer 消费队列:4成功
producer 插入队列:5成功
consumer 消费队列:5成功5秒中后生产和消费线程停止线程结束
producer 停止生产表示FLAGfalse生产介绍consumer 消费失败队列中已为空退出45_Callable接口
Callable接口是一种让线程执行完成后能够返回结果的。
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;class MyThread implements CallableInteger {Overridepublic Integer call() throws Exception {System.out.println(Thread.currentThread().getName() come in Callable);TimeUnit.SECONDS.sleep(2);return 1024;}
}public class CallableDemo {public static void main(String[] args) throws InterruptedException, ExecutionException {FutureTaskInteger futureTask new FutureTask(new MyThread());new Thread(futureTask, A).start();new Thread(futureTask, B).start();//多个线程执行 一个FutureTask的时候只会计算一次// 输出FutureTask的返回值System.out.println(result FutureTask futureTask.get());}}
46_线程池使用及优势
线程池做的工作主要是控制运行的线程的数量处理过程中将任务放入队列然后在线程创建后启动这些任务如果线程数量超过了最大数量超出数量的线程排队等候等其它线程执行完毕再从队列中取出任务来执行。
它的主要特点为线程复用控制最大并发数管理线程。
优点
降低资源消耗。通过重复利用己创建的线程降低线程创建和销毁造成的消耗。提高响应速度。当任务到达时任务可以不需要的等到线程创建就能立即执行。提高线程的可管理性。线程是稀缺资源如果无限制的创建不仅会消耗系统资源还会降低系统的稳定性使用线程池可以进行统一的分配调优和监控。
47_线程池3个常用方式
Java中的线程池是通过Executor框架实现的该框架中用到了ExecutorExecutorsExecutorServiceThreadPoolExecutor这几个类。 了解
Executors.newScheduledThreadPool()Executors.newWorkStealingPool(int) - Java8新增使用目前机器上可用的处理器作为它的并行级别
重点
Executors.newSingleThreadExecutor()
public static ExecutorService newSingleThreadExecutor() {return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueueRunnable()));
}主要特点如下
创建一个单线程化的线程池它只会用唯一的工作线程来执行任务保证所有任务按照指定顺序执行。newSingleThreadExecutor将corePoolSize和maximumPoolSize都设置为1它使用的LinkedBlockingQueue。
Executors.newFixedThreadPool(int)
public static ExecutorService newFixedThreadPool(int nThreads) {return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueueRunnable());
}主要特点如下
创建一个定长线程池可控制线程最大并发数超出的线程会在队列中等待。newFixedThreadPool创建的线程池corePoolSize和maximumPoolSize值是相等的它使用的LinkedBlockingQueue。
Executors.newCachedThreadPool()
public static ExecutorService newCachedThreadPool() {return new ThreadPoolExecutor(0, Integer.MAX_VALUE,60L, TimeUnit.SECONDS,new SynchronousQueueRunnable());
}主要特点如下
创建一个可缓存线程池如果线程池长度超过处理需要可灵活回收空闲线程若无可回收则新建线程。newCachedThreadPool将corePoolSize设置为0将maximumPoolSize设置为Integer.MAX_VALUE使用的SynchronousQueue也就是说来了任务就创建线程运行当线程空闲超过60秒就销毁线程。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class ThreadPoolDemo {public static void main(String[] args) {// 一池5个处理线程用池化技术一定要记得关闭
// ExecutorService threadPool Executors.newFixedThreadPool(5);// 创建一个只有一个线程的线程池
// ExecutorService threadPool Executors.newSingleThreadExecutor();// 创建一个拥有N个线程的线程池根据调度创建合适的线程ExecutorService threadPool Executors.newCachedThreadPool();// 模拟10个用户来办理业务每个用户就是一个来自外部请求线程try {// 循环十次模拟业务办理让5个线程处理这10个请求for (int i 0; i 10; i) {final int tempInt i;threadPool.execute(() - {System.out.println(Thread.currentThread().getName() \t 给用户: tempInt 办理业务);});}} catch (Exception e) {e.printStackTrace();} finally {threadPool.shutdown();}}
}输出结果
pool-1-thread-1 给用户:0 办理业务
pool-1-thread-6 给用户:5 办理业务
pool-1-thread-5 给用户:4 办理业务
pool-1-thread-2 给用户:1 办理业务
pool-1-thread-4 给用户:3 办理业务
pool-1-thread-3 给用户:2 办理业务
pool-1-thread-10 给用户:9 办理业务
pool-1-thread-9 给用户:8 办理业务
pool-1-thread-8 给用户:7 办理业务
pool-1-thread-7 给用户:6 办理业务48_线程池7大参数入门简介
public class ThreadPoolExecutor extends AbstractExecutorService {...public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueueRunnable 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;}...}49_线程池7大参数深入介绍 corePoolSize线程池中的常驻核心线程数 在创建了线程池后当有请求任务来之后就会安排池中的线程去执行请求任务近似理解为今日当值线程。当线程池中的线程数目达到corePoolSize后就会把到达的任务放到缓存队列当中。 maximumPoolSize线程池能够容纳同时执行的最大线程数此值必须大于等于1 keepAliveTime多余的空闲线程的存活时间。 当前线程池数量超过corePoolSize时当空闲时间达到keepAliveTime值时多余空闲线程会被销毁直到只剩下corePoolSize个线程为止 unitkeepAliveTime的单位。 workQueue任务队列被提交但尚未被执行的任务。 threadFactory表示生成线程池中工作线程的线程工厂用于创建线程一般用默认的即可。 handler拒绝策略表示当队列满了并且工作线程大于等于线程池的最大线程数 maximumPoolSize)。
50_线程池底层工作原理 在创建了线程池后等待提交过来的任务请求。 当调用execute()方法添加一个请求任务时线程池会做如下判断 如果正在运行的线程数量小于corePoolSize那么马上创建线程运行这个任务如果正在运行的线程数量大于或等于corePoolSize那么将这个任务放入队列如果这时候队列满了且正在运行的线程数量还小于maximumPoolSize那么还是要创建非核心线程立刻运行这个任务;如果队列满了且正在运行的线程数量大于或等于maximumPoolSize那么线程池会启动饱和拒绝策略来执行。 当一个线程完成任务时它会从队列中取下一个任务来执行。 当一个线程无事可做超过一定的时间keepAliveTime时线程池会判断: 如果当前运行的线程数大于corePoolSize那么这个线程就被停掉所以线程池的所有任务完成后它最终会收缩到corePoolSize的大小。
51_线程池的4种拒绝策略理论简介
等待队列也已经排满了再也塞不下新任务了同时线程池中的max线程也达到了无法继续为新任务服务。
这时候我们就需要拒绝策略机制合理的处理这个问题。
JDK拒绝策略
AbortPolicy默认直接抛出 RejectedExecutionException异常阻止系统正常运知。CallerRunsPolicy调用者运行一种调节机制该策略既不会抛弃任务也不会抛出异常而是将某些任务回退到调用者从而降低新任务的流量。DiscardOldestPolicy抛弃队列中等待最久的任务然后把当前任务加入队列中尝试再次提交当前任务。DiscardPolicy直接丢弃任务不予任何处理也不抛出异常。如果允许任务丢失这是最好的一种方案。
以上内置拒绝策略均实现了RejectedExecutionHandler接口。
52_线程池实际中使用哪一个
超级大坑警告你在工作中单一的/固定数的/可变的三种创建线程池的方法你用那个多
答案是一个都不用我们生产上只能使用自定义的
Executors 中JDK已经给你提供了为什么不用? 3.【强制】线程资源必须通过线程池提供不允许在应用中自行显式创建线程。 说明线程池的好处是减少在创建和销毁线程上所消耗的时间以及系统资源的开销解决资源不足的问题。 如果不使用线程池有可能造成系统创建大量同类线程而导致消耗完内存或者“过度切换”的问题。 4.【强制】线程池不允许使用 Executors 去创建而是通过 ThreadPoolExecutor 的方式这样的处理方式让写的同学更加明确线程池的运行规则规避资源耗尽的风险。 说明Executors 返回的线程池对象的弊端如下 1 FixedThreadPool 和 SingleThreadPool 允许的请求队列长度为 Integer.MAX_VALUE可能会堆积大量的请求从而导致 OOM。 2 CachedThreadPool 允许的创建线程数量为 Integer.MAX_VALUE可能会创建大量的线程从而导致 OOM。 阿里巴巴《Java 开发手册》 53_线程池的手写改造和拒绝策略
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;public class MyThreadPoolExecutorDemo {public static void doSomething(ExecutorService executorService, int numOfRequest) {try {System.out.println(((ThreadPoolExecutor)executorService).getRejectedExecutionHandler().getClass() :);TimeUnit.SECONDS.sleep(1);for (int i 0; i numOfRequest; i) {final int tempInt i;executorService.execute(() - {System.out.println(Thread.currentThread().getName() \t 给用户: tempInt 办理业务);});}TimeUnit.SECONDS.sleep(1);System.out.println(\n\n);} catch (Exception e) {System.err.println(e);} finally {executorService.shutdown();}}public static ExecutorService newMyThreadPoolExecutor(int corePoolSize,int maximumPoolSize, int blockingQueueSize, RejectedExecutionHandler handler){return new ThreadPoolExecutor(corePoolSize,maximumPoolSize,1,//keepAliveTimeTimeUnit.SECONDS,new LinkedBlockingQueue(blockingQueueSize),Executors.defaultThreadFactory(),handler);}public static void main(String[] args) {doSomething(newMyThreadPoolExecutor(2, 5, 3, new ThreadPoolExecutor.AbortPolicy()), 10);doSomething(newMyThreadPoolExecutor(2, 5, 3, new ThreadPoolExecutor.CallerRunsPolicy()), 20);doSomething(newMyThreadPoolExecutor(2, 5, 3, new ThreadPoolExecutor.DiscardOldestPolicy()), 10);doSomething(newMyThreadPoolExecutor(2, 5, 3, new ThreadPoolExecutor.DiscardPolicy()), 10);}}输出结果
class java.util.concurrent.ThreadPoolExecutor$AbortPolicy:
pool-1-thread-1 给用户:0 办理业务
pool-1-thread-3 给用户:5 办理业务java.util.concurrent.RejectedExecutionException: Task com.lun.concurrency.MyThreadPoolExecutorDemo$$Lambda$1/303563356eed1f14 rejected from java.util.concurrent.ThreadPoolExecutor7229724f[Running, pool size 5, active threads 0, queued tasks 0, completed tasks 8]pool-1-thread-2 给用户:1 办理业务
pool-1-thread-5 给用户:7 办理业务
pool-1-thread-3 给用户:3 办理业务
pool-1-thread-4 给用户:6 办理业务
pool-1-thread-1 给用户:2 办理业务
pool-1-thread-2 给用户:4 办理业务
class java.util.concurrent.ThreadPoolExecutor$CallerRunsPolicy:
pool-2-thread-1 给用户:0 办理业务
pool-2-thread-2 给用户:1 办理业务
pool-2-thread-1 给用户:2 办理业务
pool-2-thread-3 给用户:5 办理业务
pool-2-thread-3 给用户:7 办理业务
pool-2-thread-3 给用户:9 办理业务
pool-2-thread-4 给用户:6 办理业务
pool-2-thread-2 给用户:3 办理业务
pool-2-thread-5 给用户:8 办理业务
main 给用户:10 办理业务
pool-2-thread-1 给用户:4 办理业务
pool-2-thread-3 给用户:11 办理业务
pool-2-thread-4 给用户:13 办理业务
main 给用户:14 办理业务
pool-2-thread-1 给用户:12 办理业务
pool-2-thread-5 给用户:15 办理业务
pool-2-thread-2 给用户:17 办理业务
main 给用户:18 办理业务
pool-2-thread-3 给用户:16 办理业务
pool-2-thread-4 给用户:19 办理业务class java.util.concurrent.ThreadPoolExecutor$DiscardOldestPolicy:
pool-3-thread-1 给用户:0 办理业务
pool-3-thread-2 给用户:1 办理业务
pool-3-thread-1 给用户:2 办理业务
pool-3-thread-2 给用户:3 办理业务
pool-3-thread-3 给用户:5 办理业务
pool-3-thread-5 给用户:8 办理业务
pool-3-thread-2 给用户:7 办理业务
pool-3-thread-4 给用户:6 办理业务
pool-3-thread-1 给用户:4 办理业务
pool-3-thread-3 给用户:9 办理业务class java.util.concurrent.ThreadPoolExecutor$DiscardPolicy:
pool-4-thread-1 给用户:0 办理业务
pool-4-thread-2 给用户:1 办理业务
pool-4-thread-1 给用户:2 办理业务
pool-4-thread-2 给用户:3 办理业务
pool-4-thread-3 给用户:5 办理业务
pool-4-thread-3 给用户:9 办理业务
pool-4-thread-1 给用户:4 办理业务
pool-4-thread-5 给用户:8 办理业务
pool-4-thread-4 给用户:6 办理业务
pool-4-thread-2 给用户:7 办理业务
54_线程池配置合理线程数
合理配置线程池你是如何考虑的
CPU密集型
CPU密集的意思是该任务需要大量的运算而没有阻塞CPU一直全速运行。
CPU密集任务只有在真正的多核CPU上才可能得到加速(通过多线程), 而在单核CPU上无论你开几个模拟的多线程该任务都不可能得到加速因为CPU总的运算能力就那些。
CPU密集型任务配置尽可能少的线程数量
一般公式CPU核数1个线程的线程池
lO密集型
由于IO密集型任务线程并不是一直在执行任务则应配置尽可能多的线程如CPU核数 * 2。
IO密集型即该任务需要大量的IO即大量的阻塞。
在单线程上运行IO密集型的任务会导致浪费大量的CPU运算能力浪费在等待。
所以在IO密集型任务中使用多线程可以大大的加速程序运行即使在单核CPU上这种加速主要就是利用了被浪费掉的阻塞时间。
IO密集型时大部分线程都阻塞故需要多配置线程数
参考公式CPU核数/ (1-阻塞系数)
阻塞系数在0.8~0.9之间
比如8核CPU8/(1-0.9)80个线程数
55_死锁编码及定位分析
是什么
死锁是指两个或两个以上的进程在执行过程中因争夺资源而造成的一种互相等待的现象,若无外力干涉那它们都将无法推进下去如果系统资源充足进程的资源请求都能够碍到满足死锁出现的可能性就很低否则就会因争夺有限的资源而陷入死锁。 产生死锁主要原因
系统资源不足进程运行推进的顺序不合适资源分配不当
发生死锁的四个条件
互斥条件线程使用的资源至少有一个不能共享的。至少有一个线程必须持有一个资源且正在等待获取一个当前被别的线程持有的资源。资源不能被抢占。循环等待。
如何解决死锁问题
破坏发生死锁的四个条件其中之一即可。
产生死锁的代码根据发生死锁的四个条件
package com.lun.concurrency;import java.util.concurrent.TimeUnit;class MyTask implements Runnable{private Object resourceA, resourceB;public MyTask(Object resourceA, Object resourceB) {this.resourceA resourceA;this.resourceB resourceB;}Overridepublic void run() {synchronized (resourceA) {System.out.println(String.format(%s 自己持有%s尝试持有%s,// Thread.currentThread().getName(), resourceA, resourceB));try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}synchronized (resourceB) {System.out.println(String.format(%s 同时持有%s%s,// Thread.currentThread().getName(), resourceA, resourceB));}}}
}public class DeadLockDemo {public static void main(String[] args) {Object resourceA new Object();Object resourceB new Object();new Thread(new MyTask(resourceA, resourceB),Thread A).start();new Thread(new MyTask(resourceB, resourceA),Thread B).start();}
}输出结果
Thread A 自己持有java.lang.Object59d8d77尝试持有java.lang.Object7a15e6e6
Thread B 自己持有java.lang.Object7a15e6e6尝试持有java.lang.Object59d8d77程序卡死未出现同时持有的字样。
查看是否死锁工具 jps命令定位进程号 jstack找到死锁查看
C:\Users\abcjps -l
11968 com.lun.concurrency.DeadLockDemo
6100 jdk.jcmd/sun.tools.jps.Jps
6204 EclipseC:\Users\abcjstack 11968
2021-03-09 02:42:46
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.251-b08 mixed mode):DestroyJavaVM #13 prio5 os_prio0 tid0x00000000004de800 nid0x2524 waiting on condition [0
x0000000000000000]java.lang.Thread.State: RUNNABLEThread B #12 prio5 os_prio0 tid0x000000001e0a5800 nid0x6bc waiting for monitor entry [0x
000000001efae000]java.lang.Thread.State: BLOCKED (on object monitor)at com.lun.concurrency.MyTask.run(DeadLockDemo.java:27)- waiting to lock 0x000000076b431d80 (a java.lang.Object)- locked 0x000000076b431d90 (a java.lang.Object)at java.lang.Thread.run(Thread.java:748)Thread A #11 prio5 os_prio0 tid0x000000001e0a4800 nid0x650 waiting for monitor entry [0x
000000001eeae000]java.lang.Thread.State: BLOCKED (on object monitor)at com.lun.concurrency.MyTask.run(DeadLockDemo.java:27)- waiting to lock 0x000000076b431d90 (a java.lang.Object)- locked 0x000000076b431d80 (a java.lang.Object)at java.lang.Thread.run(Thread.java:748)Service Thread #10 daemon prio9 os_prio0 tid0x000000001e034000 nid0x2fb8 runnable [0x000
0000000000000]java.lang.Thread.State: RUNNABLEC1 CompilerThread3 #9 daemon prio9 os_prio2 tid0x000000001dffa000 nid0x26e8 waiting on c
ondition [0x0000000000000000]java.lang.Thread.State: RUNNABLEC2 CompilerThread2 #8 daemon prio9 os_prio2 tid0x000000001dff6000 nid0x484 waiting on co
ndition [0x0000000000000000]java.lang.Thread.State: RUNNABLEC2 CompilerThread1 #7 daemon prio9 os_prio2 tid0x000000001dfe0800 nid0x35c8 waiting on c
ondition [0x0000000000000000]java.lang.Thread.State: RUNNABLEC2 CompilerThread0 #6 daemon prio9 os_prio2 tid0x000000001dfde800 nid0x3b7c waiting on c
ondition [0x0000000000000000]java.lang.Thread.State: RUNNABLEAttach Listener #5 daemon prio5 os_prio2 tid0x000000001dfdd000 nid0x3834 waiting on cond
ition [0x0000000000000000]java.lang.Thread.State: RUNNABLESignal Dispatcher #4 daemon prio9 os_prio2 tid0x000000001dfdb000 nid0x214 runnable [0x00
00000000000000]java.lang.Thread.State: RUNNABLEFinalizer #3 daemon prio8 os_prio1 tid0x000000001df70800 nid0x2650 in Object.wait() [0x0
00000001e54f000]java.lang.Thread.State: WAITING (on object monitor)at java.lang.Object.wait(Native Method)- waiting on 0x000000076b388ee0 (a java.lang.ref.ReferenceQueue$Lock)at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:144)- locked 0x000000076b388ee0 (a java.lang.ref.ReferenceQueue$Lock)at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:165)at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:216)Reference Handler #2 daemon prio10 os_prio2 tid0x000000001c17d000 nid0x1680 in Object.wa
it() [0x000000001e44f000]java.lang.Thread.State: WAITING (on object monitor)at java.lang.Object.wait(Native Method)- waiting on 0x000000076b386c00 (a java.lang.ref.Reference$Lock)at java.lang.Object.wait(Object.java:502)at java.lang.ref.Reference.tryHandlePending(Reference.java:191)- locked 0x000000076b386c00 (a java.lang.ref.Reference$Lock)at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)VM Thread os_prio2 tid0x000000001c178000 nid0x3958 runnableGC task thread#0 (ParallelGC) os_prio0 tid0x0000000002667800 nid0xd3c runnableGC task thread#1 (ParallelGC) os_prio0 tid0x0000000002669000 nid0x297c runnableGC task thread#2 (ParallelGC) os_prio0 tid0x000000000266a800 nid0x2fd0 runnableGC task thread#3 (ParallelGC) os_prio0 tid0x000000000266c000 nid0x1c90 runnableGC task thread#4 (ParallelGC) os_prio0 tid0x000000000266f800 nid0x3614 runnableGC task thread#5 (ParallelGC) os_prio0 tid0x0000000002670800 nid0x298c runnableGC task thread#6 (ParallelGC) os_prio0 tid0x0000000002674000 nid0x2b40 runnableGC task thread#7 (ParallelGC) os_prio0 tid0x0000000002675000 nid0x25f4 runnableVM Periodic Task Thread os_prio2 tid0x000000001e097000 nid0xd54 waiting on conditionJNI global references: 5Found one Java-level deadlock:Thread B:waiting to lock monitor 0x000000001e105dc8 (object 0x000000076b431d80, a java.lang.Object),which is held by Thread A
Thread A:waiting to lock monitor 0x000000001c181828 (object 0x000000076b431d90, a java.lang.Object),which is held by Thread BJava stack information for the threads listed above:Thread B:at com.lun.concurrency.MyTask.run(DeadLockDemo.java:27)- waiting to lock 0x000000076b431d80 (a java.lang.Object)- locked 0x000000076b431d90 (a java.lang.Object)at java.lang.Thread.run(Thread.java:748)
Thread A:at com.lun.concurrency.MyTask.run(DeadLockDemo.java:27)- waiting to lock 0x000000076b431d90 (a java.lang.Object)- locked 0x000000076b431d80 (a java.lang.Object)at java.lang.Thread.run(Thread.java:748)Found 1 deadlock.C:\Users\abc
56_JVMGC下半场技术加强说明和前提知识要求
略
57_JVMGC快速回顾复习串讲
JVM内存结构
JVM体系概述 Java8以后的JVM 常见的垃圾回收算法
1.引用计数 2.复制
Java堆从GC的角度还可以细分为: 新生代(Eden 区、From Survivor 区和To Survivor 区)和老年代。 MinorGC的过程复制-清空-互换: a. Eden、SurvivorFrom复制到SurvivorTo年龄1 首先当Eden区满的时候会触发第一次GC把还活着的对象拷贝到SurvivorFrom区当Eden区再次触发GC的时候会扫描Eden区和From区域对这两个区域进行垃圾回收经过这次回收后还存活的对象,则直接复制到To区域如果有对象的年龄已经达到了老年的标准则赋值到老年代区同时把这些对象的年龄1。
b. 清空eden-SurvivorErom 然后清空Eden和Survivor From中的对象也即复制之后有交换谁空谁是To。
c. Survivor To和 Survivor From互换 最后Survivor To和Survivor From互换原SurvivorTo成为下一次GC时的Survivor From区。部分对象会在From和To区域中复制来复制去,如此交换15次(由ⅣM参数MaxTenuringThreshold决定,这个参数默认是15),最终如果还是存活,就存入到老年代。
标记清除
算法分成标记和清除两个阶段先标记出要回收的对象然后统一回收这些对象。 标记整理 58_谈谈你对GCRoots的理解
什么是垃圾
简单的说就是内存中已经不再被使用到的空间就是垃圾。
要进行垃圾回收如何判断一个对象是否可以被回收? 引用计数法 枚举根节点做可达性分析(根搜索路径)
引用计数法
Java 中引用和对象是有关联的。如果要操作对象则必须用引用进行。
因此很显然一个简单的办法是通过引用计数来判断一个对象是否可以回收。简单说给对象中添加一个引用计数器
每当有一个地方引用它计数器值加1
每当有一个引用失效时计数器值减1。
任何时刻计数器值为零的对象就是不可能再被使用的那么这个对象就是可回收对象。
那为什么主流的Java虚拟机里面都没有选用这种算法呢?其中最主要的原因是它很难解决对象之间相互循环引用的问题。
该算法存在但目前无人用了解决不掉循环引用的问题了解即可。 枚举根节点做可达性分析(根搜索路径)
为了解决引用计数法的循环引用问题Java使用了可达性分析的方法。 所谓“GC roots”或者说tracing GC的“根集合”就是一组必须活跃的引用。
基本思路就是通过一系列名为”GC Roots”的对象作为起始点从这个被称为GC Roots的对象开始向下搜索如果一个对象到GC Roots没有任何引用链相连时则说明此对象不可用。也即给定一个集合的引用作为根出发通过引用关系遍历对象图能被遍历到的可到达的对象就被判定为存活;没有被遍历到的就自然被判定为死亡。
Java中可以作为GC Roots的对象 虚拟机栈栈帧中的局部变量区也叫做局部变量表中引用的对象。 方法区中的类静态属性引用的对象。 方法区中常量引用的对象。 本地方法栈中JNI(Native方法)引用的对象。
59_JVM的标配参数和X参数
官方文档
JVM的参数类型 标配参数 -version java -version-help X参数了解 -Xint解释执行-Xcomp第一次使用就编译成本地代码-Xmixed混合模式 XX参数下一节
60_JVM的XX参数之布尔类型
公式-XX: 或者 - 某个属性值表示开启-表示关闭
如何查看一个正在运行中的java程序它的某个jvm参数是否开启具体值是多少
jps -l 查看一个正在运行中的java程序得到Java程序号。jinfo -flag PrintGCDetails (Java程序号 )查看它的某个jvm参数如PrintGCDetails 是否开启。jinfo -flags (Java程序号 )查看它的所有jvm参数
Case
是否打印GC收集细节
-XX:-PrintGCDetails-XX:PrintGCDetails
是否使用串行垃圾回收器
-XX:-UseSerialGC-XX:UserSerialGC
61_JVM的XX参数之设值类型
公式-XX:属性key属性值value
Case
-XX:MetaspaceSize128m-XX:MaxTenuringThreshold15
62_JVM的XX参数之XmsXmx坑题
两个经典参数
-Xms等价于-XX:InitialHeapSize初始大小内存默认物理内存1/64-Xmx等价于-XX:MaxHeapSize最大分配内存默认为物理内存1/4
63_JVM盘点家底查看初始默认值
查看初始默认参数值
-XX:PrintFlagsInitial
公式java -XX:PrintFlagsInitial
C:\Users\abcjava -XX:PrintFlagsInitial
[Global flags]int ActiveProcessorCount -1 {product} {default}uintx AdaptiveSizeDecrementScaleFactor 4 {product} {default}uintx AdaptiveSizeMajorGCDecayTimeScale 10 {product} {default}uintx AdaptiveSizePolicyCollectionCostMargin 50 {product} {default}uintx AdaptiveSizePolicyInitializingSteps 20 {product} {default}uintx AdaptiveSizePolicyOutputInterval 0 {product} {default}uintx AdaptiveSizePolicyWeight 10 {product} {default}
... 查看修改更新参数值
-XX:PrintFlagsFinal
公式java -XX:PrintFlagsFinal
C:\Users\abcjava -XX:PrintFlagsFinal
...size_t HeapBaseMinAddress 2147483648 {pd product} {default}bool HeapDumpAfterFullGC false {manageable} {default}bool HeapDumpBeforeFullGC false {manageable} {default}bool HeapDumpOnOutOfMemoryError false {manageable} {default}ccstr HeapDumpPath {manageable} {default}uintx HeapFirstMaximumCompactionCount 3 {product} {default}uintx HeapMaximumCompactionInterval 20 {product} {default}uintx HeapSearchSteps 3 {product} {default}size_t HeapSizePerGCThread 43620760 {product} {default}bool IgnoreEmptyClassPaths false {product} {default}bool IgnoreUnrecognizedVMOptions false {product} {default}uintx IncreaseFirstTierCompileThresholdAt 50 {product} {default}bool IncrementalInline true {C2 product} {default}size_t InitialBootClassLoaderMetaspaceSize 4194304 {product} {default}uintx InitialCodeCacheSize 2555904 {pd product} {default}size_t InitialHeapSize : 268435456 {product} {ergonomic}
...表示默认:表示修改过的。
64_JVM盘点家底查看修改变更值
PrintFlagsFinal举例运行java命令的同时打印出参数
java -XX:PrintFlagsFinal -XX:MetaspaceSize512m HelloWorld
...size_t MetaspaceSize : 536870912 {pd product} {default}
...打印命令行参数
-XX:PrintCommandLineFlags
C:\Users\abcjava -XX:PrintCommandLineFlags -version
-XX:ConcGCThreads2 -XX:G1ConcRefinementThreads8 -XX:GCDrainStackTargetSize64 -XX:InitialHeapSize266613056 -XX:MarkStackSize4
194304 -XX:MaxHeapSize4265808896 -XX:MinHeapSize6815736 -XX:PrintCommandLineFlags -XX:ReservedCodeCacheSize251658240 -XX:Seg
mentedCodeCache -XX:UseCompressedClassPointers -XX:UseCompressedOops -XX:UseG1GC -XX:-UseLargePagesIndividualAllocation
openjdk version 15.0.1 2020-10-20
OpenJDK Runtime Environment (build 15.0.19-18)
OpenJDK 64-Bit Server VM (build 15.0.19-18, mixed mode)65_堆内存初始大小快速复习
JDK 1.8之后将最初的永久代取消了由元空间取代。 在Java8中永久代已经被移除被一个称为元空间的区域所取代。元空间的本质和永久代类似。
元空间(Java8)与永久代(Java7)之间最大的区别在于永久带使用的JVM的堆内存但是Java8以后的元空间并不在虚拟机中而是使用本机物理内存。
因此默认情况下元空间的大小仅受本地内存限制。类的元数据放入native memory字符串池和类的静态变量放入java堆中这样可以加载多少类的元数据就不再由MaxPermSize控制而由系统的实际可用空间来控制。
public class JVMMemorySizeDemo {public static void main(String[] args) throws InterruptedException {// 返回Java虚拟机中内存的总量long totalMemory Runtime.getRuntime().totalMemory();// 返回Java虚拟机中试图使用的最大内存量long maxMemory Runtime.getRuntime().maxMemory();System.out.println(String.format(TOTAL_MEMORY(-Xms): %d B, %.2f MB., totalMemory, totalMemory / 1024.0 / 1024));System.out.println(String.format(MAX_MEMORY(-Xmx): %d B, %.2f MB., maxMemory, maxMemory / 1024.0 / 1024));}
}输出结果
TOTAL_MEMORY(-Xms): 257425408 B, 245.50 MB.
MAX_MEMORY(-Xmx): 3793747968 B, 3618.00 MB.66_常用基础参数栈内存Xss讲解
设置单个线程栈的大小一般默认为512k~1024K
等价于-XX:ThreadStackSize -XX:ThreadStackSizesize Sets the thread stack size (in bytes). Append the letter k or K to indicate kilobytes, m or M to indicate megabytes, g or G to indicate gigabytes. The default value depends on virtual memory. The following examples show how to set the thread stack size to 1024 KB in different units: -XX:ThreadStackSize1m
-XX:ThreadStackSize1024k
-XX:ThreadStackSize1048576This option is equivalent to -Xss. 官方文档 67_常用基础参数元空间MetaspaceSize讲解
-Xmn设置年轻代大小
-XX:MetaspaceSize 设置元空间大小
元空间的本质和永久代类似都是对JVM规范中方法区的实现。不过元空间与永久代之间最大的区别在于元空间并不在虚拟机中而是使用本地内存。因此默认情况下元空间的大小仅受本地内存限制
典型设置案例
-Xms128m -Xmx4096m -Xss1024k -XX:MetaspaceSize512m -XX:PrintCommandLineFlags -XX:PrintGCDetails-XX:UseSerialGC
68_常用基础参数PrintGCDetails回收前后对比讲解
-XX:PrintGCDetails 输出详细GC收集日志信息
设置参数 -Xms10m -Xmx10m -XX:PrintGCDetails 运行以下程序
import java.util.concurrent.TimeUnit;public class PrintGCDetailsDemo {public static void main(String[] args) throws InterruptedException {byte[] byteArray new byte[10 * 1024 * 1024];TimeUnit.SECONDS.sleep(Integer.MAX_VALUE);}
}输出结果
[GC (Allocation Failure) [PSYoungGen: 778K-480K(2560K)] 778K-608K(9728K), 0.0029909 secs] [Times: user0.00 sys0.00, real0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 480K-480K(2560K)] 608K-616K(9728K), 0.0007890 secs] [Times: user0.00 sys0.00, real0.00 secs]
[Full GC (Allocation Failure) [PSYoungGen: 480K-0K(2560K)] [ParOldGen: 136K-518K(7168K)] 616K-518K(9728K), [Metaspace: 2644K-2644K(1056768K)], 0.0058272 secs] [Times: user0.00 sys0.00, real0.01 secs]
[GC (Allocation Failure) [PSYoungGen: 0K-0K(2560K)] 518K-518K(9728K), 0.0002924 secs] [Times: user0.00 sys0.00, real0.00 secs]
[Full GC (Allocation Failure) [PSYoungGen: 0K-0K(2560K)] [ParOldGen: 518K-506K(7168K)] 518K-506K(9728K), [Metaspace: 2644K-2644K(1056768K)], 0.0056906 secs] [Times: user0.01 sys0.00, real0.01 secs]
Exception in thread main java.lang.OutOfMemoryError: Java heap spaceat com.lun.jvm.PrintGCDetailsDemo.main(PrintGCDetailsDemo.java:9)
HeapPSYoungGen total 2560K, used 61K [0x00000000ffd00000, 0x0000000100000000, 0x0000000100000000)eden space 2048K, 3% used [0x00000000ffd00000,0x00000000ffd0f748,0x00000000fff00000)from space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000)to space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)ParOldGen total 7168K, used 506K [0x00000000ff600000, 0x00000000ffd00000, 0x00000000ffd00000)object space 7168K, 7% used [0x00000000ff600000,0x00000000ff67ea58,0x00000000ffd00000)Metaspace used 2676K, capacity 4486K, committed 4864K, reserved 1056768Kclass space used 285K, capacity 386K, committed 512K, reserved 1048576K 69_常用基础参数SurvivorRatio讲解 调节新生代中 eden 和 S0、S1的空间比例默认为 -XX:SuriviorRatio8Eden:S0:S1 8:1:1
假如设置成 -XX:SurvivorRatio4则为 Eden:S0:S1 4:1:1
SurvivorRatio值就是设置eden区的比例占多少S0和S1相同。 ratio 英 [ˈreɪʃiəʊ] 美 [ˈreɪʃioʊ] n. 比率;比例 70_常用基础参数NewRatio讲解
配置年轻代new 和老年代old 在堆结构的占比
默认-XX:NewRatio2 新生代占1老年代2年轻代占整个堆的1/3
-XX:NewRatio4新生代占1老年代占4年轻代占整个堆的1/5
NewRadio值就是设置老年代的占比剩下的1个新生代。
新生代特别小会造成频繁的进行GC收集。
71_常用基础参数MaxTenuringThreshold讲解
晋升到老年代的对象年龄。
SurvivorTo和SurvivorFrom互换原SurvivorTo成为下一次GC时的SurvivorFrom区部分对象会在From和To区域中复制来复制去如此交换15次由JVM参数MaxTenuringThreshold决定这个参数默认为15最终如果还是存活就存入老年代。
这里就是调整这个次数的默认是15并且设置的值 在 0~15之间。
-XX:MaxTenuringThreshold0设置垃圾最大年龄。如果设置为0的话则年轻对象不经过Survivor区直接进入老年代。对于年老代比较多的应用可以提高效率。如果将此值设置为一个较大的值则年轻代对象会在Survivor区进行多次复制这样可以增加对象再年轻代的存活时间增加在年轻代即被回收的概念。 tenure 英 [ˈtenjə®] 美 [ˈtenjər] n. 任期占有 vt. 授予…终身职位 threshold 英 [ˈθreʃhəʊld] 美 [ˈθreʃhoʊld] n. 门槛;门口;阈;界;起始点;开端;起点;入门 72_强引用Reference
Reference类以及继承派生的类 当内存不足JVM开始垃圾回收对于强引用的对象就算是出现了OOM也不会对该对象进行回收死都不收。
// 这样定义的默认就是强应用
Object obj1 new Object();强引用是我们最常见的普通对象引用只要还有强引用指向一个对象就能表明对象还“活着”垃圾收集器不会碰这种对象。在Java中最常见的就是强引用把一个对象赋给一个引用变量这个引用变量就是一个强引用。当一个对象被强引用变量引用时它处于可达状态它是不可能被垃圾回收机制回收的即使该对象以后永远都不会被用到JVM也不会回收。因此强引用是造成Java内存泄漏的主要原因之一。
对于一个普通的对象如果没有其他的引用关系只要超过了引用的作用域或者显式地将相应强引用赋值为 null一般认为就是可以被垃圾收集的了(当然具体回收时机还是要看垃圾收集策略)。
73_软引用SoftReference
软引用是一种相对强引用弱化了一些的引用需要用java.lang.ref.SoftReference类来实现可以让对象豁免一些垃圾收集。
对于只有软引用的对象来说
当系统内存充足时它不会被回收当系统内存不足时它会被回收。
软引用通常用在对内存敏感的程序中比如高速缓存就有用到软引用内存够用的时候就保留不够用就回收!
当内存充足的时候软引用不用回收
public class SoftReferenceDemo {/*** 内存够用的时候* -XX:PrintGCDetails*/public static void softRefMemoryEnough() {// 创建一个强应用Object o1 new Object();// 创建一个软引用SoftReferenceObject softReference new SoftReference(o1);System.out.println(o1);System.out.println(softReference.get());o1 null;// 手动GCSystem.gc();System.out.println(o1);System.out.println(softReference.get());}/*** JVM配置故意产生大对象并配置小的内存让它的内存不够用了导致OOM看软引用的回收情况* -Xms5m -Xmx5m -XX:PrintGCDetails*/public static void softRefMemoryNoEnough() {System.out.println();// 创建一个强应用Object o1 new Object();// 创建一个软引用SoftReferenceObject softReference new SoftReference(o1);System.out.println(o1);System.out.println(softReference.get());o1 null;// 模拟OOM自动GCtry {// 创建30M的大对象byte[] bytes new byte[30 * 1024 * 1024];} catch (Exception e) {e.printStackTrace();} finally {System.out.println(o1);System.out.println(softReference.get());}}public static void main(String[] args) {softRefMemoryEnough();//softRefMemoryNoEnough();}
}内存充足输出结果
java.lang.Object15db9742
java.lang.Object15db9742
[GC (System.gc()) [PSYoungGen: 2621K-728K(76288K)] 2621K-736K(251392K), 0.0011732 secs] [Times: user0.00 sys0.00, real0.00 secs]
[Full GC (System.gc()) [PSYoungGen: 728K-0K(76288K)] [ParOldGen: 8K-519K(175104K)] 736K-519K(251392K), [Metaspace: 2646K-2646K(1056768K)], 0.0048782 secs] [Times: user0.00 sys0.00, real0.00 secs]
null
java.lang.Object15db9742
HeapPSYoungGen total 76288K, used 1966K [0x000000076b380000, 0x0000000770880000, 0x00000007c0000000)eden space 65536K, 3% used [0x000000076b380000,0x000000076b56ba70,0x000000076f380000)from space 10752K, 0% used [0x000000076f380000,0x000000076f380000,0x000000076fe00000)to space 10752K, 0% used [0x000000076fe00000,0x000000076fe00000,0x0000000770880000)ParOldGen total 175104K, used 519K [0x00000006c1a00000, 0x00000006cc500000, 0x000000076b380000)object space 175104K, 0% used [0x00000006c1a00000,0x00000006c1a81e88,0x00000006cc500000)Metaspace used 2653K, capacity 4486K, committed 4864K, reserved 1056768Kclass space used 282K, capacity 386K, committed 512K, reserved 1048576K
内存不充足软引用关联对象会被回收 java.lang.Object15db9742
java.lang.Object15db9742
[GC (Allocation Failure) [PSYoungGen: 756K-496K(1536K)] 756K-600K(5632K), 0.0009017 secs] [Times: user0.00 sys0.00, real0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 496K-480K(1536K)] 600K-624K(5632K), 0.0006772 secs] [Times: user0.00 sys0.00, real0.00 secs]
[Full GC (Allocation Failure) [PSYoungGen: 480K-0K(1536K)] [ParOldGen: 144K-519K(4096K)] 624K-519K(5632K), [Metaspace: 2646K-2646K(1056768K)], 0.0055489 secs] [Times: user0.00 sys0.00, real0.01 secs]
[GC (Allocation Failure) [PSYoungGen: 0K-0K(1536K)] 519K-519K(5632K), 0.0002674 secs] [Times: user0.00 sys0.00, real0.00 secs]
[Full GC (Allocation Failure) [PSYoungGen: 0K-0K(1536K)] [ParOldGen: 519K-507K(4096K)] 519K-507K(5632K), [Metaspace: 2646K-2646K(1056768K)], 0.0052951 secs] [Times: user0.11 sys0.00, real0.01 secs]
null
null
Exception in thread main java.lang.OutOfMemoryError: Java heap spaceat com.lun.jvm.SoftReferenceDemo.softRefMemoryNotEnough(SoftReferenceDemo.java:44)at com.lun.jvm.SoftReferenceDemo.main(SoftReferenceDemo.java:58)
HeapPSYoungGen total 1536K, used 30K [0x00000000ffe00000, 0x0000000100000000, 0x0000000100000000)eden space 1024K, 2% used [0x00000000ffe00000,0x00000000ffe07ac8,0x00000000fff00000)from space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000)to space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)ParOldGen total 4096K, used 507K [0x00000000ffa00000, 0x00000000ffe00000, 0x00000000ffe00000)object space 4096K, 12% used [0x00000000ffa00000,0x00000000ffa7edd0,0x00000000ffe00000)Metaspace used 2678K, capacity 4486K, committed 4864K, reserved 1056768Kclass space used 285K, capacity 386K, committed 512K, reserved 1048576K
回收后内存依然不足的话还是会抛异常。
74_弱引用WeakReference
弱引用需要用java.lang.ref.WeakReference类来实现它比软引用的生存期更短
对于只有弱引用的对象来说只要垃圾回收机制一运行不管JVM的内存空间是否足够都会回收该对象占用的内存。
import java.lang.ref.WeakReference;public class WeakReferenceDemo {public static void main(String[] args) {Object o1 new Object();WeakReferenceObject 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());}
}输出结果
java.lang.Object15db9742
java.lang.Object15db9742
null
null75_软引用和弱引用的适用场景
场景假如有一个应用需要读取大量的本地图片
如果每次读取图片都从硬盘读取则会严重影响性能如果一次性全部加载到内存中又可能造成内存溢出
此时使用软引用可以解决这个问题。
设计思路使用HashMap来保存图片的路径和相应图片对象关联的软引用之间的映射关系在内存不足时JVM会自动回收这些缓存图片对象所占的空间从而有效地避免了OOM的问题
MapString, SoftReferenceString imageCache new HashMapString, SoftReferenceBitmap();76_WeakHashMap案例演示和解析 Hash table based implementation of the Map interface, with weak keys. An entry in a WeakHashMap will automatically be removed when its key is no longer in ordinary use. More precisely, the presence of a mapping for a given key will not prevent the key from being discarded by the garbage collector, that is, made finalizable, finalized, and then reclaimed. When a key has been discarded its entry is effectively removed from the map, so this class behaves somewhat differently from other Map implementations. link import java.util.HashMap;
import java.util.Map;
import java.util.WeakHashMap;public class WeakHashMapDemo {public static void main(String[] args) {myHashMap();System.out.println();myWeakHashMap();}private static void myHashMap() {MapInteger, String map new HashMap();Integer key new Integer(1);String value HashMap;map.put(key, value);System.out.println(map);key null;System.gc();System.out.println(map);}private static void myWeakHashMap() {MapInteger, String map new WeakHashMap();Integer key new Integer(1);String value WeakHashMap;map.put(key, value);System.out.println(map);key null;System.gc();System.out.println(map);}
}输出结果
{1HashMap}
{1HashMap}{1WeakHashMap}
{}77_虚引用简介
虚引用需要java.lang.ref.PhantomReference类来实现。
顾名思义就是形同虚设与其他几种引用都不同虚引用并不会决定对象的生命周期。
如果一个对象仅持有虚引用那么它就和没有任何引用一样在任何时候都可能被垃圾回收器回收它不能单独使用也不能通过它访问对象虚引用必须和引用队列(ReferenceQueue)联合使用。
虚引用的主要作用是跟踪对象被垃圾回收的状态。仅仅是提供了一种确保对象被finalize以后做某些事情的机制。
PhantomReference的gei方法总是返回null因此无法访问对应的引用对象。其意义在于说明一个对象已经进入finalization阶段可以被gc回收用来实现比fihalization机制更灵活的回收操作。
换句话说设置虚引用关联的唯一目的就是在这个对象被收集器回收的时候收到一个系统通知或者后续添加进一步的处理。Java技术允许使用finalize()方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。
78_ReferenceQueue引用队列介 Reference queues, to which registered reference objects are appended by the garbage collector after the appropriate reachability changes are detected. link 回收前需要被引用的用队列保存下。
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.concurrent.TimeUnit;public class ReferenceQueueDemo {public static void main(String[] args) {Object o1 new Object();// 创建引用队列ReferenceQueueObject referenceQueue new ReferenceQueue();// 创建一个弱引用WeakReferenceObject 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();System.out.println(执行GC操作);try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(o1);System.out.println(weakReference.get());// 取队列中的内容System.out.println(referenceQueue.poll());}
}输出结果
java.lang.Object15db9742
java.lang.Object15db9742
null执行GC操作
null
null
java.lang.ref.WeakReference6d06d69c79_虚引用PhantomReference
Java提供了4种引用类型在垃圾回收的时候都有自己各自的特点。
ReferenceQueue是用来配合引用工作的没有ReferenceQueue一样可以运行。
创建引用的时候可以指定关联的队列当Gc释放对象内存的时候会将引用加入到引用队列如果程序发现某个虚引用已经被加入到引用队列那么就可以在所引用的对象的内存被回收之前采取必要的行动这相当于是一种通知机制。
当关联的引用队列中有数据的时候意味着引用指向的堆内存中的对象被回收。通过这种方式JVW允许我们在对象被销毁后做一些我们自己想做的事情。
import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;public class PhantomReferenceDemo {public static void main(String[] args) throws InterruptedException {Object o1 new Object();ReferenceQueueObject referenceQueue new ReferenceQueue();PhantomReferenceObject phantomReference new PhantomReference(o1, referenceQueue);System.out.println(o1);System.out.println(phantomReference.get());System.out.println(referenceQueue.poll());System.out.println();o1 null;System.gc();Thread.sleep(500) ;System.out.println(o1);System.out.println(phantomReference.get());System.out.println(referenceQueue.poll());}
}输出结果
java.lang.Object15db9742
null
nullnull
null
java.lang.ref.PhantomReference6d06d69c80_GCRoots和四大引用小总结 81_SOFE之StackOverflowError
JVM中常见的两种错误 StackoverFlowError java.lang.StackOverflowError OutofMemoryError java.lang.OutOfMemoryErrorjava heap spacejava.lang.OutOfMemoryErrorGC overhead limit exceeededjava.lang.OutOfMemoryErrorDirect buffer memoryjava.lang.OutOfMemoryErrorunable to create new native threadjava.lang.OutOfMemoryErrorMetaspace StackOverflowError的展现
public class StackOverflowErrorDemo {public static void main(String[] args) {main(args);}
}
输出结果
Exception in thread main java.lang.StackOverflowErrorat com.lun.jvm.StackOverflowErrorDemo.main(StackOverflowErrorDemo.java:6)at com.lun.jvm.StackOverflowErrorDemo.main(StackOverflowErrorDemo.java:6)at com.lun.jvm.StackOverflowErrorDemo.main(StackOverflowErrorDemo.java:6)...82_OOM之Java heap space
public class OOMEJavaHeapSpaceDemo {/*** * -Xms10m -Xmx10m* * param args*/public static void main(String[] args) {byte[] array new byte[80 * 1024 * 1024];}}输出结果
Exception in thread main java.lang.OutOfMemoryError: Java heap spaceat com.lun.jvm.OOMEJavaHeapSpaceDemo.main(OOMEJavaHeapSpaceDemo.java:6)83_OOM之GC overhead limit exceeded GC overhead limit exceeded 超出GC开销限制 GC回收时间过长时会抛出OutOfMemroyError。过长的定义是超过98%的时间用来做GC并且回收了不到2%的堆内存连续多次GC 都只回收了不到2%的极端情况下才会抛出。
假如不抛出GC overhead limit错误会发生什么情况呢那就是GC清理的这么点内存很快会再次填满迫使cc再次执行。这样就形成恶性循环CPU使用率一直是100%而Gc却没有任何成果。 import java.util.ArrayList;
import java.util.List;public class OOMEGCOverheadLimitExceededDemo {/*** * -Xms10m -Xmx10m -XX:MaxDirectMemorySize5m* * param args*/public static void main(String[] args) {int i 0;ListString list new ArrayList();try {while(true) {list.add(String.valueOf(i).intern());}} catch (Exception e) {System.out.println(***************i: i);e.printStackTrace();throw e;}}}输出结果
[GC (Allocation Failure) [PSYoungGen: 2048K-498K(2560K)] 2048K-1658K(9728K), 0.0033090 secs] [Times: user0.00 sys0.00, real0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 2323K-489K(2560K)] 3483K-3305K(9728K), 0.0020911 secs] [Times: user0.00 sys0.00, real0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 2537K-496K(2560K)] 5353K-4864K(9728K), 0.0025591 secs] [Times: user0.00 sys0.00, real0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 2410K-512K(2560K)] 6779K-6872K(9728K), 0.0058689 secs] [Times: user0.09 sys0.00, real0.01 secs]
[Full GC (Ergonomics) [PSYoungGen: 512K-0K(2560K)] [ParOldGen: 6360K-6694K(7168K)] 6872K-6694K(9728K), [Metaspace: 2651K-2651K(1056768K)], 0.0894928 secs] [Times: user0.42 sys0.00, real0.09 secs]
[Full GC (Ergonomics) [PSYoungGen: 2048K-1421K(2560K)] [ParOldGen: 6694K-6902K(7168K)] 8742K-8324K(9728K), [Metaspace: 2651K-2651K(1056768K)], 0.0514932 secs] [Times: user0.34 sys0.00, real0.05 secs]
[Full GC (Ergonomics) [PSYoungGen: 2048K-2047K(2560K)] [ParOldGen: 6902K-6902K(7168K)] 8950K-8950K(9728K), [Metaspace: 2651K-2651K(1056768K)], 0.0381615 secs] [Times: user0.13 sys0.00, real0.04 secs]
...省略89行...
[Full GC (Ergonomics) [PSYoungGen: 2047K-2047K(2560K)] [ParOldGen: 7044K-7044K(7168K)] 9092K-9092K(9728K), [Metaspace: 2651K-2651K(1056768K)], 0.0360935 secs] [Times: user0.25 sys0.00, real0.04 secs]
[Full GC (Ergonomics) [PSYoungGen: 2047K-2047K(2560K)] [ParOldGen: 7046K-7046K(7168K)] 9094K-9094K(9728K), [Metaspace: 2651K-2651K(1056768K)], 0.0360458 secs] [Times: user0.38 sys0.00, real0.04 secs]
[Full GC (Ergonomics) [PSYoungGen: 2047K-2047K(2560K)] [ParOldGen: 7048K-7048K(7168K)] 9096K-9096K(9728K), [Metaspace: 2651K-2651K(1056768K)], 0.0353033 secs] [Times: user0.11 sys0.00, real0.04 secs]
***************i:147041
[Full GC (Ergonomics) [PSYoungGen: 2047K-2047K(2560K)] [ParOldGen: 7050K-7048K(7168K)] 9098K-9096K(9728K), [Metaspace: 2670K-2670K(1056768K)], 0.0371397 secs] [Times: user0.22 sys0.00, real0.04 secs]
java.lang.OutOfMemoryError: GC overhead limit exceeded
[Full GC (Ergonomics) at java.lang.Integer.toString(Integer.java:401)
[PSYoungGen: 2047K-2047K(2560K)] [ParOldGen: 7051K-7050K(7168K)] 9099K-9097K(9728K), [Metaspace: 2676K-2676K(1056768K)], 0.0434184 secs] [Times: user0.38 sys0.00, real0.04 secs] at java.lang.String.valueOf(String.java:3099)at com.lun.jvm.OOMEGCOverheadLimitExceededDemo.main(OOMEGCOverheadLimitExceededDemo.java:19)
Exception in thread main java.lang.OutOfMemoryError: GC overhead limit exceeded
[Full GC (Ergonomics) [PSYoungGen: 2047K-0K(2560K)] [ParOldGen: 7054K-513K(7168K)] 9102K-513K(9728K), [Metaspace: 2677K-2677K(1056768K)], 0.0056578 secs] [Times: user0.11 sys0.00, real0.01 secs] at java.lang.Integer.toString(Integer.java:401)at java.lang.String.valueOf(String.java:3099)at com.lun.jvm.OOMEGCOverheadLimitExceededDemo.main(OOMEGCOverheadLimitExceededDemo.java:19)
HeapPSYoungGen total 2560K, used 46K [0x00000000ffd00000, 0x0000000100000000, 0x0000000100000000)eden space 2048K, 2% used [0x00000000ffd00000,0x00000000ffd0bb90,0x00000000fff00000)from space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)to space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000)ParOldGen total 7168K, used 513K [0x00000000ff600000, 0x00000000ffd00000, 0x00000000ffd00000)object space 7168K, 7% used [0x00000000ff600000,0x00000000ff6807f0,0x00000000ffd00000)Metaspace used 2683K, capacity 4486K, committed 4864K, reserved 1056768Kclass space used 285K, capacity 386K, committed 512K, reserved 1048576K
84_OOM之Direct buffer memory
导致原因
写NIO程序经常使用ByteBuffer来读取或者写入数据这是一种基于通道(Channel)与缓冲区(Buffer)的IO方式它可以使用Native函数库直接分配堆外内存然后通过一个存储在Java堆里面的DirectByteBuffer对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能因为避兔了在Java堆和Native堆中来回复制数据。
ByteBuffer.allocate(capability) 第一种方式是分配VM堆内存属于GC管辖范围由于需要拷贝所以速度相对较慢。ByteBuffer.allocateDirect(capability) 第二种方式是分配OS本地内存不属于GC管辖范围由于不需要内存拷贝所以速度相对较快。
但如果不断分配本地内存堆内存很少使用那么JV就不需要执行GCDirectByteBuffer对象们就不会被回收这时候堆内存充足但本地内存可能已经使用光了再次尝试分配本地内存就会出现OutOfMemoryError那程序就直接崩溃了。
import java.nio.ByteBuffer;
import java.util.concurrent.TimeUnit;public class OOMEDirectBufferMemoryDemo {/*** -Xms5m -Xmx5m -XX:PrintGCDetails -XX:MaxDirectMemorySize5m* * param args* throws InterruptedException*/public static void main(String[] args) throws InterruptedException {System.out.println(String.format(配置的maxDirectMemory: %.2f MB,// sun.misc.VM.maxDirectMemory() / 1024.0 / 1024));TimeUnit.SECONDS.sleep(3);ByteBuffer bb ByteBuffer.allocateDirect(6 * 1024 * 1024);}
}输出结果
[GC (Allocation Failure) [PSYoungGen: 1024K-504K(1536K)] 1024K-772K(5632K), 0.0014568 secs] [Times: user0.09 sys0.00, real0.00 secs]
配置的maxDirectMemory: 5.00 MB
[GC (System.gc()) [PSYoungGen: 622K-504K(1536K)] 890K-820K(5632K), 0.0009753 secs] [Times: user0.00 sys0.00, real0.00 secs]
[Full GC (System.gc()) [PSYoungGen: 504K-0K(1536K)] [ParOldGen: 316K-725K(4096K)] 820K-725K(5632K), [Metaspace: 3477K-3477K(1056768K)], 0.0072268 secs] [Times: user0.00 sys0.00, real0.01 secs]
Exception in thread main HeapPSYoungGen total 1536K, used 40K [0x00000000ffe00000, 0x0000000100000000, 0x0000000100000000)eden space 1024K, 4% used [0x00000000ffe00000,0x00000000ffe0a3e0,0x00000000fff00000)from space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)to space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000)ParOldGen total 4096K, used 725K [0x00000000ffa00000, 0x00000000ffe00000, 0x00000000ffe00000)object space 4096K, 17% used [0x00000000ffa00000,0x00000000ffab5660,0x00000000ffe00000)Metaspace used 3508K, capacity 4566K, committed 4864K, reserved 1056768Kclass space used 391K, capacity 394K, committed 512K, reserved 1048576K
java.lang.OutOfMemoryError: Direct buffer memoryat java.nio.Bits.reserveMemory(Bits.java:694)at java.nio.DirectByteBuffer.init(DirectByteBuffer.java:123)at java.nio.ByteBuffer.allocateDirect(ByteBuffer.java:311)at com.lun.jvm.OOMEDirectBufferMemoryDemo.main(OOMEDirectBufferMemoryDemo.java:20)
85_OOM之unable to create new native thread故障演示
不能够创建更多的新的线程了也就是说创建线程的上限达到了
高并发请求服务器时经常会出现异常java.lang.OutOfMemoryError:unable to create new native thread准确说该native thread异常与对应的平台有关
导致原因
应用创建了太多线程一个应用进程创建多个线程超过系统承载极限服务器并不允许你的应用程序创建这么多线程linux系统默认运行单个进程可以创建的线程为1024个如果应用创建超过这个数量就会报 java.lang.OutOfMemoryError:unable to create new native thread
解决方法
想办法降低你应用程序创建线程的数量分析应用是否真的需要创建这么多线程如果不是改代码将线程数降到最低对于有的应用确实需要创建很多线程远超过linux系统默认1024个线程限制可以通过修改Linux服务器配置扩大linux默认限制
public class OOMEUnableCreateNewThreadDemo {public static void main(String[] args) {for (int i 0; ; i) {System.out.println(************** i i);new Thread(() - {try {TimeUnit.SECONDS.sleep(Integer.MAX_VALUE);} catch (InterruptedException e) {e.printStackTrace();}}, String.valueOf(i)).start();}}
}上面程序在Linux OSCentOS运行会出现下列的错误线程数大概在900多个
Exception in thread main java.lang.OutOfMemoryError: unable to cerate new native thread86_OOM之unable to create new native thread上限调整
非root用户登录Linux系统CentOS测试
服务器级别调参调优
查看系统线程限制数目
ulimit -u修改系统线程限制数目
vim /etc/security/limits.d/90-nproc.conf打开后发现除了root其他账户都限制在1024个 假如我们想要张三这个用卢运行希望他生成的线程多一些我们可以如下配置 87_OOM之Metaspace
使用java -XX:PrintFlagsInitial命令查看本机的初始化参数-XX:MetaspaceSize为21810376B大约20.8M
Java 8及之后的版本使用Metaspace来替代永久代。
Metaspace是方法区在Hotspot 中的实现它与持久代最大的区别在于Metaspace并不在虚拟机内存中而是使用本地内存也即在Java8中, classe metadata(the virtual machines internal presentation of Java class)被存储在叫做Metaspace native memory。
永久代Java8后被原空向Metaspace取代了存放了以下信息
虚拟机加载的类信息常量池静态变量即时编译后的代码
模拟Metaspace空间溢出我们借助CGLib直接操作字节码运行时不断生成类往元空间灌类占据的空间总是会超过Metaspace指定的空间大小的。
首先添加CGLib依赖
!-- https://mvnrepository.com/artifact/cglib/cglib --
dependencygroupIdcglib/groupIdartifactIdcglib/artifactIdversion3.2.10/version
/dependencyimport java.lang.reflect.Method;import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;public class OOMEMetaspaceDemo {// 静态类static class OOMObject {}/*** -XX:MetaspaceSize10m -XX:MaxMetaspaceSize10m* * param args*/public static void main(final String[] args) {// 模拟计数多少次以后发生异常int i 0;try {while (true) {i;// 使用Spring的动态字节码技术Enhancer enhancer new Enhancer();enhancer.setSuperclass(OOMObject.class);enhancer.setUseCache(false);enhancer.setCallback(new MethodInterceptor() {Overridepublic 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();} finally {}}
}输出结果
发生异常的次数:569
java.lang.OutOfMemoryError: Metaspaceat net.sf.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:348)at net.sf.cglib.proxy.Enhancer.generate(Enhancer.java:492)at net.sf.cglib.core.AbstractClassGenerator$ClassLoaderData.get(AbstractClassGenerator.java:117)at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:294)at net.sf.cglib.proxy.Enhancer.createHelper(Enhancer.java:480)at net.sf.cglib.proxy.Enhancer.create(Enhancer.java:305)at com.lun.jvm.OOMEMetaspaceDemo.main(OOMEMetaspaceDemo.java:37)88_垃圾收集器回收种类
GC算法(引用计数/复制/标清/标整)是内存回收的方法论垃圾收集器就是算法落地实现。
因为目前为止还没有完美的收集器出现更加没有万能的收集器只是针对具体应用最合适的收集器进行分代收集
4种主要垃圾收集器
SerialParallelCMSG1 89_串行并行并发G1四大垃圾回收方式
串行垃级回收器(Serial) - 它为单线程环境设计且值使用一个线程进行垃圾收集会暂停所有的用户线程只有当垃圾回收完成时才会重新唤醒主线程继续执行。所以不适合服务器环境。并行垃圾回收器(Parallel) - 多个垃圾收集线程并行工作此时用户线程也是阻塞的适用于科学计算 / 大数据处理等弱交互场景也就是说Serial 和 Parallel其实是类似的不过是多了几个线程进行垃圾收集但是主线程都会被暂停但是并行垃圾收集器处理时间肯定比串行的垃圾收集器要更短。并发垃圾回收器(CMS) - 用户线程和垃圾收集线程同时执行不一定是并行可能是交替执行不需要停顿用户线程互联网公司都在使用适用于响应时间有要求的场景。G1垃圾回收器 - G1垃圾回收器将堆内存分割成不同的区域然后并发的对其进行垃圾回收。ZGCJava 11的了解
串行并行并发GC小总结G1稍后 90_如何查看默认的垃圾收集器
java -XX:PrintCommandLineFlags -version输出结果
C:\Users\abcjava -XX:PrintCommandLineFlags -version
-XX:InitialHeapSize266613056 -XX:MaxHeapSize4265808896 -XX:PrintCommandLineFlags -XX:UseCompressedClassPointers -XX:UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:UseParallelGC
java version 1.8.0_251
Java(TM) SE Runtime Environment (build 1.8.0_251-b08)
Java HotSpot(TM) 64-Bit Server VM (build 25.251-b08, mixed mode)从结果看到-XX:UseParallelGC也就是说默认的垃圾收集器是并行垃圾回收器。
或者
jps -l得出Java程序号
jinfo -flags (Java程序号)91_JVM默认的垃圾收集器有哪些
Java中一共有7大垃圾收集器 年轻代GC UserSerialGC串行垃圾收集器UserParallelGC并行垃圾收集器UseParNewGC年轻代的并行垃圾回收器 老年代GC UserSerialOldGC串行老年代垃圾收集器已经被移除UseParallelOldGC老年代的并行垃圾回收器UseConcMarkSweepGCCMS并发标记清除 老嫩通吃 UseG1GCG1垃圾收集器
92_GC之7大垃圾收集器概述
垃圾收集器就来具体实现这些GC算法并实现内存回收。
不同厂商、不同版本的虚拟机实现差别很大HotSpot中包含的收集器如下图所示 新生代 串行GC(Serial)/(Serial Copying) 并行GC(ParNew) 并行回收GC(Parallel)/(Parallel Scavenge)
93_GC之约定参数说明
DefNewDefault New GenerationTenuredOldParNewParallel New GenerationPSYoungGenParallel ScavengeParOldGenParallel Old Generation
Server/Client模式分别是什么意思 使用范围一般使用Server模式Client模式基本不会使用 操作系统 32位的Window操作系统不论硬件如何都默认使用Client的JVM模式32位的其它操作系统2G内存同时有2个cpu以上用Server模式低于该配置还是Client模式64位只有Server模式
C:\Users\abcjava -version
java version 1.8.0_251
Java(TM) SE Runtime Environment (build 1.8.0_251-b08)
Java HotSpot(TM) 64-Bit Server VM (build 25.251-b08, mixed mode)94_GC之Serial收集器 serial 英 [ˈsɪəriəl] 美 [ˈsɪriəl] n. 电视连续剧;广播连续剧;杂志连载小说 adj. 顺序排列的;排成系列的;连续的;多次的;以连续剧形式播出的;连载的 一句话一个单线程的收集器在进行垃圾收集时候必须暂停其他所有的工作线程直到它收集结束。 STW: Stop The World
串行收集器是最古老最稳定以及效率高的收集器只使用一个线程去回收但其在进行垃圾收集过程中可能会产生较长的停顿Stop-The-World”状态)。虽然在收集垃圾过程中需要暂停所有其他的工作线程但是它简单高效对于限定单个CPU环境来说没有线程交互的开销可以获得最高的单线程垃圾收集效率因此Serial垃圾收集器依然是java虚拟机运行在Client模式下默认的新生代垃圾收集器。
对应JVM参数是-XX:UseSerialGC
开启后会使用Serial(Young区用) Serial Old(Old区用)的收集器组合
表示新生代、老年代都会使用串行回收收集器新生代使用复制算法老年代使用标记-整理算法
public class GCDemo {public static void main(String[] args) throws InterruptedException {Random rand new Random(System.nanoTime());try {String str Hello, World;while(true) {str str rand.nextInt(Integer.MAX_VALUE) rand.nextInt(Integer.MAX_VALUE);}}catch (Throwable e) {e.printStackTrace();}}
}VM参数启用UseSerialGC
-Xms10m -Xmx10m -XX:PrintGCDetails -XX:PrintCommandLineFlags -XX:UseSerialGC输出结果
-XX:InitialHeapSize10485760 -XX:MaxHeapSize10485760 -XX:PrintCommandLineFlags -XX:PrintGCDetails -XX:UseCompressedClassPointers -XX:UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:UseSerialGC
[GC (Allocation Failure) [DefNew: 2346K-320K(3072K), 0.0012956 secs] 2346K-1030K(9920K), 0.0013536 secs] [Times: user0.00 sys0.00, real0.00 secs]
[GC (Allocation Failure) [DefNew: 2888K-0K(3072K), 0.0013692 secs] 3598K-2539K(9920K), 0.0014059 secs] [Times: user0.02 sys0.00, real0.00 secs]
[GC (Allocation Failure) [DefNew: 2065K-0K(3072K), 0.0011613 secs] 4604K-4550K(9920K), 0.0011946 secs] [Times: user0.00 sys0.00, real0.00 secs]
[GC (Allocation Failure) [DefNew: 2056K-0K(3072K), 0.0010394 secs] 6606K-6562K(9920K), 0.0010808 secs] [Times: user0.00 sys0.00, real0.00 secs]
[GC (Allocation Failure) [DefNew: 2011K-2011K(3072K), 0.0000124 secs][Tenured: 6562K-2537K(6848K), 0.0021691 secs] 8574K-2537K(9920K), [Metaspace: 2658K-2658K(1056768K)], 0.0024399 secs] [Times: user0.00 sys0.00, real0.00 secs]
[GC (Allocation Failure) [DefNew: 2059K-2059K(3072K), 0.0000291 secs][Tenured: 6561K-6561K(6848K), 0.0012330 secs] 8620K-6561K(9920K), [Metaspace: 2658K-2658K(1056768K)], 0.0012888 secs] [Times: user0.00 sys0.00, real0.00 secs]
[Full GC (Allocation Failure) [Tenured: 6561K-6547K(6848K), 0.0017784 secs] 6561K-6547K(9920K), [Metaspace: 2658K-2658K(1056768K)], 0.0018111 secs] [Times: user0.00 sys0.00, real0.00 secs]
java.lang.OutOfMemoryError: Java heap spaceat java.util.Arrays.copyOfRange(Arrays.java:3664)at java.lang.String.init(String.java:207)at java.lang.StringBuilder.toString(StringBuilder.java:407)at com.lun.jvm.GCDemo.main(GCDemo.java:23)
Heapdef new generation total 3072K, used 105K [0x00000000ff600000, 0x00000000ff950000, 0x00000000ff950000)eden space 2752K, 3% used [0x00000000ff600000, 0x00000000ff61a7c8, 0x00000000ff8b0000)from space 320K, 0% used [0x00000000ff8b0000, 0x00000000ff8b0000, 0x00000000ff900000)to space 320K, 0% used [0x00000000ff900000, 0x00000000ff900000, 0x00000000ff950000)tenured generation total 6848K, used 6547K [0x00000000ff950000, 0x0000000100000000, 0x0000000100000000)the space 6848K, 95% used [0x00000000ff950000, 0x00000000fffb4c30, 0x00000000fffb4e00, 0x0000000100000000)Metaspace used 2689K, capacity 4486K, committed 4864K, reserved 1056768Kclass space used 286K, capacity 386K, committed 512K, reserved 1048576K
DefNewDefault New GenerationTenuredOld
95_GC之ParNew收集器
一句话使用多线程进行垃圾回收在垃圾收集时会Stop-The-World暂停其他所有的工作线程直到它收集结束。 ParNew收集器其实就是Serial收集器新生代的并行多线程版本最常见的应用场景是配合老年代的CMS GC工作其余的行为和Seria收集器完全一样ParNew垃圾收集器在垃圾收集过程中同样也要暂停所有其他的工作线程。它是很多Java虚拟机运行在Server模式下新生代的默认垃圾收集器。
常用对应JVM参数-XX:UseParNewGC启用ParNew收集器只影响新生代的收集不影响老年代。
开启上述参数后会使用ParNew(Young区) Serial Old的收集器组合新生代使用复制算法老年代采用标记-整理算法
但是ParNewTenured这样的搭配Java8已经不再被推荐 Java HotSpot™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. 备注-XX:ParallelGCThreads限制线程数量默认开启和CPU数目相同的线程数。
复用上一节的GCDemo
VM参数
-Xms10m -Xmx10m -XX:PrintGCDetails -XX:PrintCommandLineFlags -XX:UseParNewGC输出结果
-XX:InitialHeapSize10485760 -XX:MaxHeapSize10485760 -XX:PrintCommandLineFlags -XX:PrintGCDetails -XX:UseCompressedClassPointers -XX:UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:UseParNewGC
[GC (Allocation Failure) [ParNew: 2702K-320K(3072K), 0.0007029 secs] 2702K-1272K(9920K), 0.0007396 secs] [Times: user0.02 sys0.00, real0.00 secs]
[GC (Allocation Failure) [ParNew: 2292K-37K(3072K), 0.0010829 secs] 3244K-2774K(9920K), 0.0011000 secs] [Times: user0.00 sys0.00, real0.00 secs]
[GC (Allocation Failure) [ParNew: 2005K-9K(3072K), 0.0008401 secs] 4742K-5624K(9920K), 0.0008605 secs] [Times: user0.00 sys0.00, real0.00 secs]
[GC (Allocation Failure) [ParNew: 1974K-1974K(3072K), 0.0000136 secs][Tenured: 5615K-3404K(6848K), 0.0021646 secs] 7589K-3404K(9920K), [Metaspace: 2658K-2658K(1056768K)], 0.0022520 secs] [Times: user0.00 sys0.00, real0.00 secs]
[GC (Allocation Failure) [ParNew: 1918K-2K(3072K), 0.0008094 secs] 5322K-5324K(9920K), 0.0008273 secs] [Times: user0.00 sys0.00, real0.00 secs]
[GC (Allocation Failure) [ParNew: 1970K-1970K(3072K), 0.0000282 secs][Tenured: 5322K-4363K(6848K), 0.0018652 secs] 7292K-4363K(9920K), [Metaspace: 2658K-2658K(1056768K)], 0.0019205 secs] [Times: user0.00 sys0.00, real0.00 secs]
[Full GC (Allocation Failure) [Tenured: 4363K-4348K(6848K), 0.0023131 secs] 4363K-4348K(9920K), [Metaspace: 2658K-2658K(1056768K)], 0.0023358 secs] [Times: user0.00 sys0.00, real0.00 secs]
java.lang.OutOfMemoryError: Java heap spaceat 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.lun.jvm.GCDemo.main(GCDemo.java:22)
Heappar new generation total 3072K, used 106K [0x00000000ff600000, 0x00000000ff950000, 0x00000000ff950000)eden space 2752K, 3% used [0x00000000ff600000, 0x00000000ff61a938, 0x00000000ff8b0000)from space 320K, 0% used [0x00000000ff8b0000, 0x00000000ff8b0000, 0x00000000ff900000)to space 320K, 0% used [0x00000000ff900000, 0x00000000ff900000, 0x00000000ff950000)tenured generation total 6848K, used 4348K [0x00000000ff950000, 0x0000000100000000, 0x0000000100000000)the space 6848K, 63% used [0x00000000ff950000, 0x00000000ffd8f3a0, 0x00000000ffd8f400, 0x0000000100000000)Metaspace used 2689K, capacity 4486K, committed 4864K, reserved 1056768Kclass space used 286K, capacity 386K, committed 512K, reserved 1048576K
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
96_GC之Parallel收集器
Parallel / Parallel Scavenge Parallel Scavenge收集器类似ParNew也是一个新生代垃圾收集器使用复制算法也是一个并行的多线程的垃圾收集器俗称吞吐量优先收集器。一句话串行收集器在新生代和老年代的并行化。
它重点关注的是
可控制的吞吐量(Thoughput运行用户代码时间(运行用户代码时间垃圾收集时间),也即比如程序运行100分钟垃圾收集时间1分钟吞吐量就是99% )。高吞吐量意味着高效利用CPU的时间它多用于在后台运算而不需要太多交互的任务。
自适应调节策略也是ParallelScavenge收集器与ParNew收集器的一个重要区别。(自适应调节策略:虚拟机会根据当前系统的运行情况收集性能监控信息动态调整这些参数以提供最合适的停顿时间-XX:MaxGCPauseMillis或最大的吞吐量。
常用JVM参数-XX:UseParallelGC或-XX:UseParallelOldGC可互相激活使用Parallel Scanvenge收集器。
开启该参数后新生代使用复制算法老年代使用标记-整理算法。
多说一句-XX:ParallelGCThreads数字N 表示启动多少个GC线程 cpu8 N 5/8 cpu8 N实际个数
复用上一节GCDemo
VM参数
-Xms10m -Xmx10m -XX:PrintGCDetails -XX:PrintCommandLineFlags -XX:UseParallelGC输出结果
-XX:InitialHeapSize10485760 -XX:MaxHeapSize10485760 -XX:PrintCommandLineFlags -XX:PrintGCDetails -XX:UseCompressedClassPointers -XX:UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:UseParallelGC
[GC (Allocation Failure) [PSYoungGen: 2009K-503K(2560K)] 2009K-803K(9728K), 0.7943182 secs] [Times: user0.00 sys0.00, real0.79 secs]
[GC (Allocation Failure) [PSYoungGen: 2272K-432K(2560K)] 2572K-2214K(9728K), 0.0020218 secs] [Times: user0.00 sys0.00, real0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 2448K-352K(2560K)] 4230K-3122K(9728K), 0.0017173 secs] [Times: user0.11 sys0.02, real0.00 secs]
[Full GC (Ergonomics) [PSYoungGen: 1380K-0K(2560K)] [ParOldGen: 6722K-2502K(7168K)] 8102K-2502K(9728K), [Metaspace: 2657K-2657K(1056768K)], 0.0039763 secs] [Times: user0.00 sys0.00, real0.00 secs]
[Full GC (Ergonomics) [PSYoungGen: 2016K-0K(2560K)] [ParOldGen: 6454K-6454K(7168K)] 8471K-6454K(9728K), [Metaspace: 2658K-2658K(1056768K)], 0.0049598 secs] [Times: user0.00 sys0.00, real0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 0K-0K(2560K)] 6454K-6454K(9728K), 0.0008614 secs] [Times: user0.00 sys0.00, real0.00 secs]
[Full GC (Allocation Failure) [PSYoungGen: 0K-0K(2560K)] [ParOldGen: 6454K-6440K(7168K)] 6454K-6440K(9728K), [Metaspace: 2658K-2658K(1056768K)], 0.0055542 secs] [Times: user0.00 sys0.00, real0.01 secs]
java.lang.OutOfMemoryError: Java heap spaceat java.util.Arrays.copyOfRange(Arrays.java:3664)at java.lang.String.init(String.java:207)at java.lang.StringBuilder.toString(StringBuilder.java:407)at com.lun.jvm.GCDemo.main(GCDemo.java:22)
HeapPSYoungGen total 2560K, used 82K [0x00000000ffd00000, 0x0000000100000000, 0x0000000100000000)eden space 2048K, 4% used [0x00000000ffd00000,0x00000000ffd14810,0x00000000fff00000)from space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)to space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000)ParOldGen total 7168K, used 6440K [0x00000000ff600000, 0x00000000ffd00000, 0x00000000ffd00000)object space 7168K, 89% used [0x00000000ff600000,0x00000000ffc4a1c8,0x00000000ffd00000)Metaspace used 2689K, capacity 4486K, committed 4864K, reserved 1056768Kclass space used 286K, capacity 386K, committed 512K, reserved 1048576K
97_GC之ParallelOld收集器
Parallel Old收集器是Parallel Scavenge的老年代版本使用多线程的标记-整理算法Parallel Old收集器在JDK1.6才开始提供。
在JDK1.6之前新生代使用ParallelScavenge收集器只能搭配年老代的Serial Old收集器只能保证新生代的吞吐量优先无法保证整体的吞吐量。在JDK1.6之前Parallel Scavenge Serial Old )
Parallel Old 正是为了在年老代同样提供吞吐量优先的垃圾收集器如果系统对吞吐量要求比较高JDK1.8后可以优先考虑新生代Parallel Scavenge和年老代Parallel Old收集器的搭配策略。在JDK1.8及后〈Parallel Scavenge Parallel Old )
JVM常用参数-XX:UseParallelOldGC使用Parallel Old收集器设置该参数后新生代Parallel老年代Parallel Old。
复用上一节GCDemo
VM参数
-Xms10m -Xmx10m -XX:PrintGCDetails -XX:PrintCommandLineFlags -XX:UseParallelOldGC输出结果
-XX:InitialHeapSize10485760 -XX:MaxHeapSize10485760 -XX:PrintCommandLineFlags -XX:PrintGCDetails -XX:UseCompressedClassPointers -XX:UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:UseParallelOldGC
[GC (Allocation Failure) [PSYoungGen: 1979K-480K(2560K)] 1979K-848K(9728K), 0.0007724 secs] [Times: user0.00 sys0.00, real0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 2205K-480K(2560K)] 2574K-2317K(9728K), 0.0008700 secs] [Times: user0.02 sys0.00, real0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 2446K-496K(2560K)] 4284K-3312K(9728K), 0.0010374 secs] [Times: user0.00 sys0.00, real0.00 secs]
[Full GC (Ergonomics) [PSYoungGen: 1499K-0K(2560K)] [ParOldGen: 6669K-2451K(7168K)] 8168K-2451K(9728K), [Metaspace: 2658K-2658K(1056768K)], 0.0043327 secs] [Times: user0.00 sys0.00, real0.00 secs]
[Full GC (Ergonomics) [PSYoungGen: 1966K-0K(2560K)] [ParOldGen: 6304K-6304K(7168K)] 8270K-6304K(9728K), [Metaspace: 2658K-2658K(1056768K)], 0.0021269 secs] [Times: user0.00 sys0.00, real0.00 secs]
[GC (Allocation Failure) [PSYoungGen: 0K-0K(2560K)] 6304K-6304K(9728K), 0.0004841 secs] [Times: user0.00 sys0.00, real0.00 secs]
[Full GC (Allocation Failure) [PSYoungGen: 0K-0K(2560K)] [ParOldGen: 6304K-6290K(7168K)] 6304K-6290K(9728K), [Metaspace: 2658K-2658K(1056768K)], 0.0058149 secs] [Times: user0.11 sys0.00, real0.01 secs]
java.lang.OutOfMemoryError: Java heap spaceat java.util.Arrays.copyOfRange(Arrays.java:3664)at java.lang.String.init(String.java:207)at java.lang.StringBuilder.toString(StringBuilder.java:407)at com.lun.jvm.GCDemo.main(GCDemo.java:22)
HeapPSYoungGen total 2560K, used 81K [0x00000000ffd00000, 0x0000000100000000, 0x0000000100000000)eden space 2048K, 3% used [0x00000000ffd00000,0x00000000ffd14768,0x00000000fff00000)from space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000)to space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000)ParOldGen total 7168K, used 6290K [0x00000000ff600000, 0x00000000ffd00000, 0x00000000ffd00000)object space 7168K, 87% used [0x00000000ff600000,0x00000000ffc24b70,0x00000000ffd00000)Metaspace used 2689K, capacity 4486K, committed 4864K, reserved 1056768Kclass space used 286K, capacity 386K, committed 512K, reserved 1048576K
98_GC之CMS收集器
CMS收集器(Concurrent Mark Sweep并发标记清除是一种以获取最短回收停顿时间为目标的收集器。
适合应用在互联网站或者B/S系统的服务器上这类应用尤其重视服务器的响应速度希望系统停顿时间最短。
CMS非常适合地内存大、CPU核数多的服务器端应用也是G1出现之前大型应用的首选收集器。 Concurrent Mark Sweep并发标记清除并发收集低停顿,并发指的是与用户线程一起执行 开启该收集器的JVM参数-XX:UseConcMarkSweepGC开启该参数后会自动将-XX:UseParNewGC打开。
开启该参数后使用ParNewYoung区用 CMSOld区用 Serial Old的收集器组合Serial Old将作为CMS出错的后备收集器。
4步过程 初始标记CMS initial mark - 只是标记一下GC Roots能直接关联的对象速度很快仍然需要暂停所有的工作线程。 并发标记CMS concurrent mark和用户线程一起 - 进行GC Roots跟踪的过程和用户线程一起工作不需要暂停工作线程。主要标记过程标记全部对象。 重新标记CMS remark- 为了修正在并发标记期间因用户程序继续运行而导致标记产生变动的那一部分对象的标记记录仍然需要暂停所有的工作线程。由于并发标记时用户线程依然运行因此在正式清理前再做修正。 并发清除CMS concurrent sweep - 清除GCRoots不可达对象和用户线程一起工作不需要暂停工作线程。基于标记结果直接清理对象由于耗时最长的并发标记和并发清除过程中垃圾收集线程可以和用户现在一起并发工作所以总体上来看CMS 收集器的内存回收和用户线程是一起并发地执行。 优点并发收集低停顿。
缺点并发执行对CPU资源压力大采用的标记清除算法会导致大量碎片。
由于并发进行CMS在收集与应用线程会同时会增加对堆内存的占用也就是说CMS必须要在老年代堆内存用尽之前完成垃圾回收否则CMS回收失败时将触发担保机制串行老年代收集器将会以STW的方式进行一次GC从而造成较大停顿时间。
标记清除算法无法整理空间碎片老年代空间会随着应用时长被逐步耗尽最后将不得不通过担保机制对堆内存进行压缩。CMS也提供了参数-XX:CMSFullGCsBeForeCompaction(默认O即每次都进行内存整理)来指定多少次CMS收集之后进行一次压缩的Full GC。
复用上一节GCDemo
VM参数
-Xms10m -Xmx10m -XX:PrintGCDetails -XX:PrintCommandLineFlags -XX:UseConcMarkSweepGC输出结果
-XX:InitialHeapSize10485760 -XX:MaxHeapSize10485760 -XX:MaxNewSize3497984 -XX:MaxTenuringThreshold6 -XX:NewSize3497984 -XX:OldPLABSize16 -XX:OldSize6987776 -XX:PrintCommandLineFlags -XX:PrintGCDetails -XX:UseCompressedClassPointers -XX:UseCompressedOops -XX:UseConcMarkSweepGC -XX:-UseLargePagesIndividualAllocation -XX:UseParNewGC
[GC (Allocation Failure) [ParNew: 2274K-319K(3072K), 0.0016975 secs] 2274K-1043K(9920K), 0.0017458 secs] [Times: user0.03 sys0.00, real0.00 secs]
[GC (Allocation Failure) [ParNew: 2844K-8K(3072K), 0.0010921 secs] 3568K-2287K(9920K), 0.0011138 secs] [Times: user0.00 sys0.00, real0.00 secs]
[GC (Allocation Failure) [ParNew: 2040K-2K(3072K), 0.0037625 secs] 4318K-4257K(9920K), 0.0037843 secs] [Times: user0.00 sys0.00, real0.00 secs]
[GC (CMS Initial Mark) [1 CMS-initial-mark: 4255K(6848K)] 6235K(9920K), 0.0003380 secs] [Times: user0.00 sys0.00, real0.00 secs]
[CMS-concurrent-mark-start]
[GC (Allocation Failure) [ParNew: 2024K-2K(3072K), 0.0013295 secs] 6279K-6235K(9920K), 0.0013596 secs] [Times: user0.00 sys0.00, real0.00 secs]
[GC (Allocation Failure) [ParNew: 1979K-1979K(3072K), 0.0000116 secs][CMS[CMS-concurrent-mark: 0.001/0.003 secs] [Times: user0.00 sys0.00, real0.00 secs] (concurrent mode failure): 6233K-2508K(6848K), 0.0031737 secs] 8212K-2508K(9920K), [Metaspace: 2657K-2657K(1056768K)], 0.0032232 secs] [Times: user0.00 sys0.00, real0.00 secs]
[GC (Allocation Failure) [ParNew: 2025K-2025K(3072K), 0.0000154 secs][CMS: 6462K-6461K(6848K), 0.0020534 secs] 8488K-6461K(9920K), [Metaspace: 2658K-2658K(1056768K)], 0.0021033 secs] [Times: user0.01 sys0.00, real0.00 secs]
[Full GC (Allocation Failure) [CMS: 6461K-6448K(6848K), 0.0020383 secs] 6461K-6448K(9920K), [Metaspace: 2658K-2658K(1056768K)], 0.0020757 secs] [Times: user0.00 sys0.00, real0.00 secs]
[GC (CMS Initial Mark) [1 CMS-initial-mark: 6448K(6848K)] 6448K(9920K), 0.0001419 secs] [Times: user0.00 sys0.00, real0.00 secs]
[CMS-concurrent-mark-start]
[CMS-concurrent-mark: 0.001/0.001 secs] [Times: user0.00 sys0.00, real0.00 secs]
[CMS-concurrent-preclean-start]
[CMS-concurrent-preclean: 0.000/0.000 secs] [Times: user0.00 sys0.00, real0.00 secs]
[GC (CMS Final Remark) [YG occupancy: 50 K (3072 K)][Rescan (parallel) , 0.0002648 secs][weak refs processing, 0.0000173 secs][class unloading, 0.0002671 secs][scrub symbol table, 0.0004290 secs][scrub string table, 0.0001593 secs][1 CMS-remark: 6448K(6848K)] 6499K(9920K), 0.0012107 secs] [Times: user0.00 sys0.00, real0.00 secs]
[CMS-concurrent-sweep-start]
java.lang.OutOfMemoryError: Java heap space
[CMS-concurrent-sweep: 0.000/0.000 secs] [Times: user0.00 sys0.00, real0.00 secs]
[CMS-concurrent-reset-start]
[CMS-concurrent-reset: 0.000/0.000 secs] [Times: user0.00 sys0.00, real0.00 secs] at java.util.Arrays.copyOfRange(Arrays.java:3664)at java.lang.String.init(String.java:207)at java.lang.StringBuilder.toString(StringBuilder.java:407)at com.lun.jvm.GCDemo.main(GCDemo.java:22)
Heappar new generation total 3072K, used 106K [0x00000000ff600000, 0x00000000ff950000, 0x00000000ff950000)eden space 2752K, 3% used [0x00000000ff600000, 0x00000000ff61a820, 0x00000000ff8b0000)from space 320K, 0% used [0x00000000ff8b0000, 0x00000000ff8b0000, 0x00000000ff900000)to space 320K, 0% used [0x00000000ff900000, 0x00000000ff900000, 0x00000000ff950000)concurrent mark-sweep generation total 6848K, used 6447K [0x00000000ff950000, 0x0000000100000000, 0x0000000100000000)Metaspace used 2689K, capacity 4486K, committed 4864K, reserved 1056768Kclass space used 286K, capacity 386K, committed 512K, reserved 1048576K99_GC之SerialOld收集器
Serial Old是Serial垃圾收集器老年代版本它同样是个单线程的收集器使用标记-整理算法这个收集器也主要是运行在 Client默认的java虚拟机默认的年老代垃圾收集器。
在Server模式下主要有两个用途(了解版本已经到8及以后):
在JDK1.5之前版本中与新生代的Parallel Scavenge 收集器搭配使用。(Parallel Scavenge Serial Old )作为老年代版中使用CMS收集器的后备垃圾收集方案。
复用上一节GCDemo
VM参数
-Xms10m -Xmx10m -XX:PrintGCDetails -XX:PrintCommandLineFlags -XX:UseSerialOldGC输出结果
Unrecognized VM option UseSerialOldGC
Did you mean (/-)UseSerialGC?在Java8中-XX:UseSerialOldGC不起作用。
100_GC之如何选择垃圾收集器
组合的选择
单CPU或者小内存单机程序 -XX:UseSerialGC 多CPU需要最大的吞吐量如后台计算型应用 -XX:UseParallelGC这两个相互激活-XX:UseParallelOldGC 多CPU追求低停顿时间需要快速响应如互联网应用 -XX:UseConcMarkSweepGC-XX:ParNewGC
参数新生代垃圾收集器新生代算法老年代垃圾收集器老年代算法-XX:UseSerialGCSerialGC复制SerialOldGC标记整理-XX:UseParNewGCParNew复制SerialOldGC标记整理-XX:UseParallelGCParallel [Scavenge]复制Parallel Old标记整理-XX:UseConcMarkSweepGCParNew复制CMS Serial Old的收集器组合Serial Old作为CMS出错的后备收集器标记清除-XX:UseG1GCG1整体上采用标记整理算法局部复制
101_GC之G1收集器
复用上一节GCDemo
VM参数
-Xms10m -Xmx10m -XX:PrintGCDetails -XX:PrintCommandLineFlags -XX:UseG1GC输出结果
-XX:InitialHeapSize10485760 -XX:MaxHeapSize10485760 -XX:PrintCommandLineFlags -XX:PrintGCDetails -XX:UseCompressedClassPointers -XX:UseCompressedOops -XX:UseG1GC -XX:-UseLargePagesIndividualAllocation
[GC pause (G1 Humongous Allocation) (young) (initial-mark), 0.0015787 secs][Parallel Time: 0.8 ms, GC Workers: 8][GC Worker Start (ms): Min: 106.4, Avg: 106.5, Max: 106.5, Diff: 0.1][Ext Root Scanning (ms): Min: 0.2, Avg: 0.3, Max: 0.5, Diff: 0.4, Sum: 2.2][Update RS (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0][Processed Buffers: Min: 0, Avg: 0.0, Max: 0, Diff: 0, Sum: 0][Scan RS (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0][Code Root Scanning (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0][Object Copy (ms): Min: 0.0, Avg: 0.3, Max: 0.3, Diff: 0.3, Sum: 2.1][Termination (ms): Min: 0.0, Avg: 0.0, Max: 0.1, Diff: 0.1, Sum: 0.4][Termination Attempts: Min: 1, Avg: 5.3, Max: 10, Diff: 9, Sum: 42][GC Worker Other (ms): Min: 0.0, Avg: 0.0, Max: 0.1, Diff: 0.0, Sum: 0.3][GC Worker Total (ms): Min: 0.6, Avg: 0.6, Max: 0.7, Diff: 0.1, Sum: 4.9][GC Worker End (ms): Min: 107.1, Avg: 107.1, Max: 107.1, Diff: 0.0][Code Root Fixup: 0.0 ms][Code Root Purge: 0.0 ms][Clear CT: 0.3 ms][Other: 0.5 ms][Choose CSet: 0.0 ms][Ref Proc: 0.2 ms][Ref Enq: 0.0 ms][Redirty Cards: 0.3 ms][Humongous Register: 0.0 ms][Humongous Reclaim: 0.0 ms][Free CSet: 0.0 ms][Eden: 4096.0K(4096.0K)-0.0B(4096.0K) Survivors: 0.0B-1024.0K Heap: 7073.4K(10.0M)-2724.8K(10.0M)][Times: user0.02 sys0.02, real0.00 secs]
[GC concurrent-root-region-scan-start]
[GC concurrent-root-region-scan-end, 0.0004957 secs]
[GC concurrent-mark-start]
[GC concurrent-mark-end, 0.0001071 secs]
[GC remark [Finalize Marking, 0.0001876 secs] [GC ref-proc, 0.0002450 secs] [Unloading, 0.0003675 secs], 0.0011690 secs][Times: user0.00 sys0.00, real0.00 secs]
[GC cleanup 4725K-4725K(10M), 0.0004907 secs][Times: user0.00 sys0.00, real0.00 secs]
[GC pause (G1 Humongous Allocation) (young), 0.0009748 secs][Parallel Time: 0.6 ms, GC Workers: 8][GC Worker Start (ms): Min: 111.8, Avg: 111.9, Max: 112.2, Diff: 0.5][Ext Root Scanning (ms): Min: 0.0, Avg: 0.1, Max: 0.2, Diff: 0.2, Sum: 0.8][Update RS (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0][Processed Buffers: Min: 0, Avg: 0.0, Max: 0, Diff: 0, Sum: 0][Scan RS (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0][Code Root Scanning (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0][Object Copy (ms): Min: 0.0, Avg: 0.2, Max: 0.3, Diff: 0.3, Sum: 1.7][Termination (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.2][Termination Attempts: Min: 1, Avg: 3.3, Max: 5, Diff: 4, Sum: 26][GC Worker Other (ms): Min: 0.1, Avg: 0.1, Max: 0.1, Diff: 0.0, Sum: 0.8][GC Worker Total (ms): Min: 0.1, Avg: 0.5, Max: 0.6, Diff: 0.5, Sum: 3.6][GC Worker End (ms): Min: 112.3, Avg: 112.3, Max: 112.4, Diff: 0.0][Code Root Fixup: 0.0 ms][Code Root Purge: 0.0 ms][Clear CT: 0.1 ms][Other: 0.2 ms][Choose CSet: 0.0 ms][Ref Proc: 0.1 ms][Ref Enq: 0.0 ms][Redirty Cards: 0.1 ms][Humongous Register: 0.0 ms][Humongous Reclaim: 0.0 ms][Free CSet: 0.0 ms][Eden: 1024.0K(4096.0K)-0.0B(4096.0K) Survivors: 1024.0K-1024.0K Heap: 6808.1K(10.0M)-2595.2K(10.0M)][Times: user0.00 sys0.00, real0.00 secs]
[GC pause (G1 Humongous Allocation) (young) (initial-mark), 0.0006211 secs][Parallel Time: 0.2 ms, GC Workers: 8][GC Worker Start (ms): Min: 113.3, Avg: 113.3, Max: 113.4, Diff: 0.1][Ext Root Scanning (ms): Min: 0.0, Avg: 0.1, Max: 0.2, Diff: 0.2, Sum: 1.0][Update RS (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0][Processed Buffers: Min: 0, Avg: 0.1, Max: 1, Diff: 1, Sum: 1][Scan RS (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0][Code Root Scanning (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0][Object Copy (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0][Termination (ms): Min: 0.0, Avg: 0.0, Max: 0.1, Diff: 0.1, Sum: 0.3][Termination Attempts: Min: 1, Avg: 1.0, Max: 1, Diff: 0, Sum: 8][GC Worker Other (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0][GC Worker Total (ms): Min: 0.1, Avg: 0.2, Max: 0.2, Diff: 0.1, Sum: 1.4][GC Worker End (ms): Min: 113.5, Avg: 113.5, Max: 113.5, Diff: 0.0][Code Root Fixup: 0.0 ms][Code Root Purge: 0.0 ms][Clear CT: 0.1 ms][Other: 0.3 ms][Choose CSet: 0.0 ms][Ref Proc: 0.1 ms][Ref Enq: 0.0 ms][Redirty Cards: 0.1 ms][Humongous Register: 0.0 ms][Humongous Reclaim: 0.0 ms][Free CSet: 0.0 ms][Eden: 0.0B(4096.0K)-0.0B(2048.0K) Survivors: 1024.0K-1024.0K Heap: 4595.9K(10.0M)-4557.3K(10.0M)][Times: user0.00 sys0.00, real0.00 secs]
[GC concurrent-root-region-scan-start]
[GC pause (G1 Humongous Allocation) (young)[GC concurrent-root-region-scan-end, 0.0001112 secs]
[GC concurrent-mark-start]
, 0.0006422 secs][Root Region Scan Waiting: 0.0 ms][Parallel Time: 0.2 ms, GC Workers: 8][GC Worker Start (ms): Min: 114.2, Avg: 114.3, Max: 114.4, Diff: 0.2][Ext Root Scanning (ms): Min: 0.0, Avg: 0.1, Max: 0.1, Diff: 0.1, Sum: 0.7][Update RS (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0][Processed Buffers: Min: 0, Avg: 0.1, Max: 1, Diff: 1, Sum: 1][Scan RS (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0][Code Root Scanning (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0][Object Copy (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0][Termination (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.1][Termination Attempts: Min: 1, Avg: 1.0, Max: 1, Diff: 0, Sum: 8][GC Worker Other (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0][GC Worker Total (ms): Min: 0.0, Avg: 0.1, Max: 0.2, Diff: 0.2, Sum: 0.9][GC Worker End (ms): Min: 114.4, Avg: 114.4, Max: 114.4, Diff: 0.0][Code Root Fixup: 0.0 ms][Code Root Purge: 0.0 ms][Clear CT: 0.1 ms][Other: 0.3 ms][Choose CSet: 0.0 ms][Ref Proc: 0.1 ms][Ref Enq: 0.0 ms][Redirty Cards: 0.1 ms][Humongous Register: 0.0 ms][Humongous Reclaim: 0.0 ms][Free CSet: 0.0 ms][Eden: 0.0B(2048.0K)-0.0B(2048.0K) Survivors: 1024.0K-1024.0K Heap: 4557.3K(10.0M)-4547.6K(10.0M)][Times: user0.00 sys0.00, real0.00 secs]
[Full GC (Allocation Failure) 4547K-4527K(10M), 0.0023437 secs][Eden: 0.0B(2048.0K)-0.0B(3072.0K) Survivors: 1024.0K-0.0B Heap: 4547.6K(10.0M)-4527.6K(10.0M)], [Metaspace: 2658K-2658K(1056768K)][Times: user0.00 sys0.00, real0.00 secs]
[Full GC (Allocation Failure) 4527K-4513K(10M), 0.0021281 secs][Eden: 0.0B(3072.0K)-0.0B(3072.0K) Survivors: 0.0B-0.0B Heap: 4527.6K(10.0M)-4514.0K(10.0M)], [Metaspace: 2658K-2658K(1056768K)][Times: user0.00 sys0.00, real0.00 secs]
[GC concurrent-mark-abort]
java.lang.OutOfMemoryError: Java heap spaceat 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.lun.jvm.GCDemo.main(GCDemo.java:22)
Heapgarbage-first heap total 10240K, used 4513K [0x00000000ff600000, 0x00000000ff700050, 0x0000000100000000)region size 1024K, 1 young (1024K), 0 survivors (0K)Metaspace used 2689K, capacity 4486K, committed 4864K, reserved 1056768Kclass space used 286K, capacity 386K, committed 512K, reserved 1048576K
以前收集器特点
年轻代和老年代是各自独立且连续的内存块年轻代收集使用单edens0s1进行复机算法老年代收集必须扫描整个老年代区域都是以尽可能少而快速地执行GC为设计原则。
G1是什么
G1 (Garbage-First收集器是一款面向服务端应用的收集器
The Garbage-First (G1) collector is a server-style garbage collector, targeted for multi-processor machines with large memories. It meets garbage collection(GC) pause time goals with a high probability, while achieving high throughput. The G1 garbage collector is fully supported in Oracle JDK 7 update 4 and later releases. The G1 collectoris designed for applications that:
Can operate concurrently with applications threads like the CMS collector.Compact free space without lengthy GC induced pause times.Need more predictable GC pause durations.Do not want to sacrifice a lot of throughput performance.Do not require a much larger Java heap.
从官网的描述中我们知道G1是一种服务器端的垃圾收集器应用在多处理器和大容量内存环境中在实现高吞吐量的同时尽可能的满足垃圾收集暂停时间的要求。另外它还具有以下特性:
像CMS收集器一样能与应用程序线程并发执行。
整理空闲空间更快。
需要更多的时间来预测GC停顿时间。
不希望牺牲大量的吞吐性能。
不需要更大的Java Heap。
G1收集器的设计目标是取代CMS收集器它同CMS相比在以下方面表现的更出色
G1是一个有整理内存过程的垃圾收集器不会产生很多内存碎片。
G1的Stop The World(STW)更可控G1在停顿时间上添加了预测机制用户可以指定期望停顿时间。
CMS垃圾收集器虽然减少了暂停应用程序的运行时间但是它还是存在着内存碎片问题。于是为了去除内存碎片问题同时又保留CMS垃圾收集器低暂停时间的优点JAVA7发布了一个新的垃圾收集器-G1垃圾收集器。
G1是在2012年才在jdk1.7u4中可用。oracle官方计划在JDK9中将G1变成默认的垃圾收集器以替代CMS。它是一款面向服务端应用的收集器主要应用在多CPU和大内存服务器环境下极大的减少垃圾收集的停顿时间全面提升服务器的性能逐步替换java8以前的CMS收集器。
主要改变是EdenSurvivor和Tenured等内存区域不再是连续的了而是变成了一个个大小一样的region ,每个region从1M到32M不等。一个region有可能属于EdenSurvivor或者Tenured内存区域。
特点
G1能充分利用多CPU、多核环境硬件优势尽量缩短STW。G1整体上采用标记-整理算法局部是通过复制算法不会产生内存碎片。宏观上看G1之中不再区分年轻代和老年代。把内存划分成多个独立的子区域(Region)可以近似理解为一个围棋的棋盘。G1收集器里面讲整个的内存区都混合在一起了但其本身依然在小范围内要进行年轻代和老年代的区分保留了新生代和老年代但它们不再是物理隔离的而是一部分Region的集合且不需要Region是连续的也就是说依然会采用不同的GC方式来处理不同的区域。G1虽然也是分代收集器但整个内存分区不存在物理上的年轻代与老年代的区别也不需要完全独立的survivor(to space)堆做复制准备。G1只有逻辑上的分代概念或者说每个分区都可能随G1的运行在不同代之间前后切换。
102_GC之G1底层原理
Region区域化垃圾收集器 - 最大好处是化整为零避免全内存扫描只需要按照区域来进行扫描即可。
区域化内存划片Region整体编为了一些列不连续的内存区域避免了全内存区的GC操作。
核心思想是将整个堆内存区域分成大小相同的子区域(Region)在JVM启动时会自动设置这些子区域的大小在堆的使用上G1并不要求对象的存储一定是物理上连续的只要逻辑上连续即可每个分区也不会固定地为某个代服务可以按需在年轻代和老年代之间切换。启动时可以通过参数-XX:G1HeapRegionSizen可指定分区大小1MB~32MB且必须是2的幂默认将整堆划分为2048个分区。
大小范围在1MB~32MB最多能设置2048个区域也即能够支持的最大内存为32MB∗204865536MB64G32MB*204865536MB64G32MB∗204865536MB64G内存 humongous 英 [hjuːˈmʌŋɡəs] 美 [hjuːˈmʌŋɡəs] adj. 巨大的;庞大的 G1算法将堆划分为若干个区域(Region它仍然属于分代收集器。
这些Region的一部分包含新生代新生代的垃圾收集依然采用暂停所有应用线程的方式将存活对象拷贝到老年代或者Survivor空间。
这些Region的一部分包含老年代G1收集器通过将对象从一个区域复制到另外一个区域完成了清理工作。这就意味着在正常的处理过程中G1完成了堆的压缩至少是部分堆的压缩这样也就不会有CMS内存碎片问题的存在了。
在G1中还有一种特殊的区域叫Humongous区域。
如果一个对象占用的空间超过了分区容量50%以上G1收集器就认为这是一个巨型对象。这些巨型对象默认直接会被分配在年老代但是如果它是一个短期存在的巨型对象就会对垃圾收集器造成负面影响。
为了解决这个问题G1划分了一个Humongous区它用来专门存放巨型对象。如果一个H区装不下一个巨型对象那么G1会寻找连续的H分区来存储。为了能找到连续的H区有时候不得不启动Full GC。
回收步骤
G1收集器下的Young GC
针对Eden区进行收集Eden区耗尽后会被触发主要是小区域收集形成连续的内存块避免内存碎片
Eden区的数据移动到Survivor区假如出现Survivor区空间不够Eden区数据会部会晋升到Old区。Survivor区的数据移动到新的Survivor区部会数据晋升到Old区。最后Eden区收拾干净了GC结束用户的应用程序继续执行。 4步过程
初始标记只标记GC Roots能直接关联到的对象并发标记进行GC Roots Tracing的过程最终标记修正并发标记期间因程序运行导致标记发生变化的那一部分对象筛选回收根据时间来进行价值最大化的回收 103_GC之G1参数配置及和CMS的比较
-XX:UseG1GC-XX:G1HeapRegionSizen设置的G1区域的大小。值是2的幂范围是1MB到32MB。目标是根据最小的Java堆大小划分出约2048个区域。-XX:MaxGCPauseMillisn最大GC停顿时间这是个软目标JVM将尽可能但不保证停顿小于这个时间。-XX:InitiatingHeapOccupancyPercentn堆占用了多少的时候就触发GC默认为45。-XX:ConcGCThreadsn并发GC使用的线程数。-XX:G1ReservePercentn设置作为空闲空间的预留内存百分比以降低目标空间溢出的风险默认值是10%。
开发人员仅仅需要声明以下参数即可
三步归纳开始G1设置最大内存设置最大停顿时间
-XX:UseG1GC-Xmx32g-XX:MaxGCPauseMillis100
-XX:MaxGCPauseMillisn最大GC停顿时间单位毫秒这是个软目标JVM将尽可能但不保证停顿小于这个时间
G1和CMS比较
G1不会产生内碎片是可以精准控制停顿。该收集器是把整个堆新生代、老年代划分成多个固定大小的区域每次根据允许停顿的时间去收集垃圾最多的区域。
104_JVMGC结合SpringBoot微服务优化简介
IDEA开发微服务工程。Maven进行clean package。要求微服务启动的时候同时配置我们的JVM/GC的调优参数。公式java -server jvm的各种参数 -jar 第1步上面的jar/war包名。
105_Linux命令之top
top - 整机性能查看 主要看load average, CPU, MEN三部分 load average表示系统负载即任务队列的平均长度。 三个数值分别为 1分钟、5分钟、15分钟前到现在的平均值。 load average: 如果这个数除以逻辑CPU的数量结果高于5的时候就表明系统在超负荷运转了。 Linux中top命令参数详解 uptime - 系统性能命令的精简版 106_Linux之cpu查看vmstat vmstat -n 2 3procs r运行和等待的CPU时间片的进程数原则上1核的CPU的运行队列不要超过2整个系统的运行队列不超过总核数的2倍否则代表系统压力过大我们看蘑菇博客测试服务器能发现都超过了2说明现在压力过大b等待资源的进程数比如正在等待磁盘I/O、网络I/O等 cpu us用户进程消耗CPU时间百分比us值高用户进程消耗CPU时间多如果长期大于50%优化程序sy内核进程消耗的CPU时间百分比us sy 参考值为80%如果us sy 大于80%说明可能存在CPU不足从上面的图片可以看出us sy还没有超过百分80因此说明蘑菇博客的CPU消耗不是很高id处于空闲的CPU百分比wa系统等待IO的CPU时间百分比st来自于一个虚拟机偷取的CPU时间比
107_Linux之cpu查看pidstat
查看看所有cpu核信息
mpstat -P ALL 2每个进程使用cpu的用量分解信息
pidstat -u 1 -p 进程编号108_Linux之内存查看free和pidstat
应用程序可用内存数
经验值 应用程序可用内存l系统物理内存70%内存充足 应用程序可用内存/系统物理内存20%内存不足需要增加内存 20%应用程序可用内存/系统物理内存70%内存基本够用 m/g兆/吉
查看额外
pidstat -p 进程号 -r 采样间隔秒数109_Linux之硬盘查看df
查看磁盘剩余空间数 110_Linux之磁盘IO查看iostat和pidstat
磁盘I/O性能评估 磁盘块设备分布
rkB/s每秒读取数据量kB;wkB/s每秒写入数据量kB;svctm lO请求的平均服务时间单位毫秒;await l/O请求的平均等待时间单位毫秒;值越小性能越好;util一秒中有百分几的时间用于I/O操作。接近100%时表示磁盘带宽跑满需要优化程序或者增加磁盘;rkB/s、wkB/s根据系统应用不同会有不同的值但有规律遵循:长期、超大数据读写肯定不正常需要优化程序读取。svctm的值与await的值很接近表示几乎没有IO等待磁盘性能好。如果await的值远高于svctm的值则表示IO队列等待太长需要优化程序或更换更快磁盘。
111_Linux之网络IO查看ifstat
默认本地没有下载ifstat
wget http://gael.roualland.free.fr/lifstat/ifstat-1.1.tar.gz
tar -xzvf ifstat-1.1.tar.gz
cd ifstat-1.1
./configure
make
make install查看网络IO
各个网卡的in、out
观察网络负载情况程序
网络读写是否正常
程序网络I/O优化增加网络I/O带宽 112_CPU占用过高的定位分析思路
结合Linux和JDK命令一块分析
案例步骤
先用top命令找出CPU占比最高的 ps -ef或者jps进一步定位得知是一个怎么样的一个后台程序作搞屎棍 定位到具体线程或者代码 ps -mp 进程 -o THREAD,tid,time -m 显示所有的线程-p pid进程使用cpu的时间-o 该参数后是用户自定义格式 将需要的线程ID转换为16进制格式英文小写格式命令printf %x 172 将172转换为十六进制jstack 进程ID | grep tid16进制线程ID小写英文-A60 ps - process status -A Display information about other users’ processes, including those without controlling terminals. -e Identical to -A. -f Display the uid, pid, parent pid, recent CPU usage, process start time, controlling tty, elapsed CPU usage, and the associated command. If the -u option is also used, display the user name rather then the numeric uid. When -o or -O is used to add to the display following -f, the command field is not truncated as severely as it is in other formats. ps -ef中的e、f是什么含义 对于JDK自带的JVM监控和性能分析工具用过哪些?一般你是怎么用的?link
113_GitHub骚操作之开启
略
114_GitHub骚操作之常用词
常用词含义
watch会持续收到该项目的动态fork复制其个项目到自己的Github仓库中star可以理解为点赞clone将项目下载至本地follow关注你感兴趣的作者会收到他们的动态
115_GitHub骚操作之in限制搜索
in关键词限制搜索范围
公式 xxx(关键词) in:name或description或readme xxx in:name 项目名包含xxx的xxx in:description 项目描述包含xxx的xxx in:readme 项目的readme文件中包含xxx的组合使用 组合使用 搜索项目名或者readme中包含秒杀的项目xxx in:name,readme
116_GitHub骚操作之star和fork范围搜索
公式 xxx关键字 stars 通配符 : 或者 :区间范围数字 stars:数字1…数字2 案例 查找stars数大于等于5000的springboot项目springboot stars:5000查找forks数在1000~2000之间的springboot项目springboot forks:1000…5000 组合使用 查找star大于1000fork数在500到1000的springboot项目springboot stars:1000 forks:500…1000
117_GitHub骚操作之awesome搜索
公式awesome 关键字awesome系列一般用来收集学习、工具、书籍类相关的项目搜索优秀的redis相关的项目包括框架教程等 awesome redis
118_GitHub骚操作之#L数字
一行地址后面紧跟 #L10 https://github.com/abc/abc/pom.xml#L13 多行地址后面紧跟 #Lx - #Ln https://github.com/moxi624/abc/abc/pom.xml#L13-L30
119_GitHub骚操作之T搜索
在项目仓库下按键盘T进行项目内搜索 更多github快捷键
120_GitHub骚操作之搜索区域活跃用户
location地区language语言例如location:beijing language:java