JMM(Java内存模型)
可见性:
多个线程操作同一个变量时,当其中一个线程改变了变量的值之后马上通知其他线程变量值已经被改变了就叫做可见性
原子性
不可分割
有序性
编译后的指令和源代码一样按照一定顺序排列
指令重排
volatile可以禁止指令重排
写时复制
集合
ArrayList的底层结构是Object数组:
HashSet的底层结构是HashMap:
HashSet的add方法为啥只需要放一个值,因为value是固定的。
锁
公平锁/非公平锁
可重入锁(递归锁)
自旋锁
独占锁(写锁)/共享锁(读锁)/互斥锁
CountDownLatch
CyclicBarrier
Semaphore
阻塞队列
synchronized和lock的区别
线程池
线程池总共有5种,三种常见,两种不常见
线程池优势
线程池示例代码
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 线程池示例代码
*/
public class MyThreadPoolDemo {
public static void main(String[] args) {
// ExecutorService threadPool = Executors.newFixedThreadPool(5); // 固定长度的线程池
// ExecutorService threadPool = Executors.newSingleThreadExecutor(); // 长度为1的线程池
ExecutorService threadPool = Executors.newCachedThreadPool(); // 动态扩容的线程池
try {
for (int i = 0; i < 10; i++) {
threadPool.submit(() -> {
System.out.println(Thread.currentThread().getName() + " 正在办理业务");
});
// TimeUnit.MILLISECONDS.sleep(200);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
threadPool.shutdown();
}
}
}
三种线程池创建方式源码
线程池的7大参数
线程池底层工作原理
线程池的4种拒绝策略
DiscardOldestPolicy和DiscardPolicy会直接丢弃任务
不使用上面默认的三种线程池的原因
如何合理设置线程池的参数
cpu密集型
IO密集型
死锁
死锁示例代码
import java.util.concurrent.TimeUnit;
class HoldLock implements Runnable{
private String lockA;
private String lockB;
public HoldLock(String lockA, String lockB) {
this.lockA = lockA;
this.lockB = lockB;
}
@Override
public void run() {
synchronized (lockA) {
System.out.println(Thread.currentThread().getName() + " 持有锁:" + lockA + " 等待锁:" + lockB);
try {
TimeUnit.SECONDS.sleep(2L);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
synchronized (lockB) {
System.out.println(Thread.currentThread().getName() + " 持有锁:" + lockB + " 等待锁:" + lockA);
}
}
}
}
/**
* 死锁示例代码
*/
public class DeadLockDemo {
public static void main(String[] args) {
String lockA = "lockA";
String lockB = "lockB";
new Thread(new HoldLock(lockA, lockB), "t1").start();
new Thread(new HoldLock(lockB, lockA), "t2").start();
}
}
JVM和GC
JVM体系结构
GC的作用域
常见的垃圾回收算法
如何确定GC垃圾
可以作为GC Roots的对象
上图中1和4标注的可能有问题
JVM的参数类型
JVM XX参数
boolean类型
打印GC收集细节
使用串行垃圾回收器
KV设值类型
jinfo查看默认JVM参数
macOS下JDK11版本的
window下JDK8版本的
-Xms和-Xmx参数
查看jdk安装后所有的默认参数
命令:java -XX:+PrintFlagsInitial
查看jdk安装之后调整过的所有参数
命令:java -XX:+PrintFlagsFinal -version
运行Java代码的同时并修改参数
命令:java -XX:+PrintCommandLineFlags -version
常用JVM参数
常用的调参示例(注意:此示例仅仅只是说明该调哪些参数,具体值需要根据实际情况进行调整):
如下参数一般不进行调整,采用默认即可
注意:MaxTenuringThreshold在Java8中只能设置成0-15之间
Java11中范围为0-16
-XX:+PrintGCDetails
jvm运行参数:-Xms10m -Xmx10m -XX:+PrintGCDetails
Java8垃圾回收日志
Java8之前垃圾回收日志解析
GC类型
FULL GC类型
强引用、软引用、弱引用、虚引用
整体架构图
强引用
示例代码:
/**
* 强引用示例
*/
public class StrongReferenceDemo {
public static void main(String[] args) {
Object obj1 = new Object(); // 这样定义的默认就是强引用
Object obj2 = obj1; // obj2引用赋值
obj1 = null; // 置空
System.gc();
System.out.println("obj2 = " + obj2);
}
}
软引用
示例代码:
import java.lang.ref.SoftReference;
/**
* 软引用示例
*/
public class SoftReferenceDemo {
public static void enough() {
Object o1 = new Object();
SoftReference<Object> softReference = new SoftReference<>(o1);
System.out.println("o1 = " + o1);
System.out.println("softReference.get() = " + softReference.get());
o1 = null;
System.gc();
System.out.println("o1 = " + o1);
System.out.println("softReference.get() = " + softReference.get());
}
public static void notEnough() {
Object o1 = new Object();
SoftReference<Object> softReference = new SoftReference<>(o1);
System.out.println("o1 = " + o1);
System.out.println("softReference.get() = " + softReference.get());
o1 = null;
try {
byte[] bytes = new byte[30 * 1024 * 1024];
} catch (Throwable throwable) {
throwable.printStackTrace();
} finally {
System.out.println("o1 = " + o1);
System.out.println("softReference.get() = " + softReference.get());
}
}
public static void main(String[] args) {
// enough();
notEnough();
}
}
notEnough方法执行jvm参数为:-Xms5m -Xmx5m -XX:+PrintGCDetails
弱引用
示例代码:
import java.lang.ref.WeakReference;
/**
* 弱引用示例
*/
public class WeakReferenceDemo {
public static void main(String[] args) {
Object o1 = new Object();
WeakReference<Object> weakReference = new WeakReference<>(o1);
System.out.println(o1);
System.out.println(weakReference.get());
o1 = null;
System.gc();
System.out.println("==========================");
System.out.println(o1);
System.out.println(weakReference.get());
}
}
软引用和弱引用适用的场景
WeakHashMap
当key置为null的时候,且垃圾回收器进行垃圾回收,那么WeakHashMap里面存放的值也会变为null
示例代码:
import java.util.HashMap;
import java.util.WeakHashMap;
/**
* WeakHashMap示例
*/
public class WeakHashMapDemo {
public static void testHashMap() {
HashMap<Integer, String> hashMap = new HashMap<>();
Integer key = new Integer(1);
String value = "HashMap";
hashMap.put(key, value);
System.out.println(hashMap);
key = null;
System.out.println(hashMap);
System.gc();
System.out.println(hashMap + " size : " + hashMap.size());
}
public static void testWeakHashMap() {
WeakHashMap<Integer, String> map = new WeakHashMap<>();
Integer key = new Integer(2);
String value = "WeakHashMap";
map.put(key, value);
System.out.println(map);
key = null;
System.out.println(map);
System.gc();
System.out.println(map + " size : " + map.size());
}
public static void main(String[] args) {
testHashMap();
System.out.println("==============");
testWeakHashMap();
}
}
Java8运行结果
Java11运行结果
神奇现象
虚引用
示例代码:
import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
/**
* 虚引用示例
*/
public class PhantomReferenceDemo {
public static void main(String[] args) {
Object o1 = new Object();
ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
PhantomReference<Object> phantomReference = new PhantomReference<>(o1, referenceQueue);
System.out.println(o1);
System.out.println(phantomReference.get());
System.out.println(referenceQueue.poll());
System.out.println("====================");
o1 = null;
System.gc();
System.out.println(o1);
System.out.println(phantomReference.get());
System.out.println(referenceQueue.poll());
}
}
ReferenceQueue
引用被GC回收前会先放入ReferenceQueue中(示例用虚引用比较直观)
示例代码:
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
/**
* ReferenceQueue示例
*/
public class ReferenceQueueDemo {
public static void main(String[] args) {
Object o1 = new Object();
ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
WeakReference<Object> weakReference = new WeakReference<>(o1, referenceQueue);
System.out.println(o1);
System.out.println(weakReference.get());
System.out.println(referenceQueue.poll());
System.out.println("====================");
o1 = null;
System.gc();
System.out.println(o1);
System.out.println(weakReference.get());
System.out.println(referenceQueue.poll());
}
}
GCRoots和四大引用小总结
OOM的认识
错误类型
StackOverflowError
示例代码:
/**
* StackOverflowError示例
* 无限递归调用可以引发StackOverflowError
*/
public class StackOverflowErrorDemo {
public static void main(String[] args) {
stackOverflowError();
}
private static void stackOverflowError() {
stackOverflowError(); // Exception in thread "main" java.lang.StackOverflowError
}
}
Java heap space
示例代码:
/**
* Java heap space 示例代码
* 运行JVM参数:-Xms10m -Xmx10m
*/
public class JavaHeapSpaceDemo {
public static void main(String[] args) {
String str = "aaa";
// Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
while (true) {
str += str + UUID.randomUUID() + new Random().nextInt(111111111);
str.intern();
}
// 或者直接创建一个大对象
// byte[] bytes = new byte[50 * 1024 * 1024];
}
}
GC overhead limit exceeded
示例代码:
import java.util.ArrayList;
import java.util.List;
/**
* GC overhead limit exceeded 示例
* JVM运行时参数:
* -Xms10m -Xmx10m -XX:+PrintGCDetails -XX:MaxDirectMemorySize=5m
* 注意:此程序在Java11上面报的错误为:java.lang.OutOfMemoryError: Java heap space,和垃圾回收器相关
* G1垃圾回收器(Java9及以后默认)报:java.lang.OutOfMemoryError: Java heap space
* Parallel垃圾回收器(Java8默认)报:java.lang.OutOfMemoryError: GC overhead limit exceeded
*/
public class GCOverheadLimitDemo {
public static void main(String[] args) {
int i = 0;
List<String> list = new ArrayList<>();
try {
while (true) {
list.add(String.valueOf(++i).intern());
}
} catch (Throwable throwable) {
System.out.println("====================== i ============ : " + i);
throwable.printStackTrace();
throw throwable;
}
}
}
Direct buffer memory
示例代码:
import java.nio.ByteBuffer;
/**
* Direct buffer memory示例
* JVM运行时参数:
* -Xms10m -Xmx10m -XX:+PrintGCDetails -XX:MaxDirectMemorySize=5m
*/
public class DirectBufferMemoryDemo {
public static void main(String[] args) {
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(6 * 1024 * 1024);
}
}
unable to create native thread
示例代码:
import java.util.concurrent.TimeUnit;
/**
* unable to create native thread 示例
* 此案例和操作系统有关,Linux最大能创建1024个线程,macOS最大能创建4096个线程。实际情况会略小于理论值。
*/
public class UnableCreateNewThreadDemo {
// Exception in thread "main" java.lang.OutOfMemoryError: unable to create native thread
public static void main(String[] args) {
for (int i = 0; ; i++) {
System.out.println("i = " + i);
new Thread(() -> {
try {
TimeUnit.SECONDS.sleep(5L);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}, String.valueOf(i)).start();
}
}
}
Linux系统查看及调整:
Metaspace
示例代码:
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* Metaspace示例
* JVM运行时参数:
* -XX:MetaspaceSize=10m -XX:MaxMetaspaceSize=10m
*/
public class MetaspaceOOMDemo {
static class OOMTest {
}
public static void main(String[] args) {
int i = 0;
try {
while (true) {
i++;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(OOMTest.class);
enhancer.setUseCache(false);
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
return methodProxy.invoke(o, args);
}
});
enhancer.create();
}
} catch (Throwable e) {
System.out.println("i = " + i);
e.printStackTrace();
}
}
}
垃圾回收器
串行垃圾回收器Serial
并行垃圾回收器Parallel
并发垃圾回收器CMS
G1垃圾回收器
查看默认的垃圾收集器
默认的垃圾收集器
jdk8中有以下6种,之前有个serial old(已废弃),jdk11增加了ZGC
垃圾收集器详解(新生代和老年代分别用可以用哪些)
部分参数预先说明
jdk server和client模式的区别
新生代串行Serial收集器
Java8垃圾回收日志
新生代并行ParNew收集器(只是新生代用并行收集器)
Java8垃圾回收日志
新生代并行Parallel/Parallel Scavenge收集器
Java8垃圾回收日志
老年代并行Parallel Old收集器
Java8垃圾回收日志
老年代并发CMS收集器(并发标记清除GC)
老年代串行Serial Old收集器
如何选择垃圾收集器(不包含G1)
G1垃圾回收器
以前垃圾回收器的特点
G1是什么
特点
底层原理
Region区域化垃圾收集器
回收步骤
4步过程
G1相关的一些参数(了解)
G1和CMS比较的优势
Linux排查系统问题常用命令
top命令
按 q 退出命令
vmstat:查看cpu相关信息
free:查看内存相关信息
df:查看硬盘相关信息
iostat:磁盘IO
ifstat:网络IO
下载安装命令集合
使用命令:ifstat 1 (后面参数是数字1,不是英文字母l)
CPU占用过高的分析思路和定位
定位到具体线程或者代码
上图(排查问题总览图)中的第四点
GitHub的骚操作
常用词
in 限制搜索范围
star和fork范围搜索
awesome加强搜索
#L : 高亮显示某一行或者某几行代码
示例:
https://github.com/BurningTomcat/wechatserver/blob/master/src/com/wechat/common/controller/CoreController.java#L29-L61
项目内搜索
进入项目后直接按 t (GitHub的快捷键)