程序计数器(寄存器)
每个线程都有自己的程序计数器
特别:1.线程私有 2.不会存在内存溢出
---------------------------
栈:数据结构 -- 先进后出
线程运行需要的内存空间 栈帧(每个方法运行时需要的 内存)
栈内存:①一次次方法调用产生的栈帧内存(方法结束后自己回收)
②不是越大越好,栈内存越大,线程数就越少(系统默认的就可以)
③线程内的私有变化线程安全(
方法内局部变量没有逃离方法的作用访问线程安全;
如果局部变量引用了对象,并逃离方法的作用方法,需要考虑线程安全)
---------------------------
栈内存溢出(Stack Overflow)
常见的 -- 方法递归
1.栈帧过多导致
2.栈帧过大导致(不大可能出现)
VM option: -Xss256k 参数设置大小
private static int count;
private static void method1(){
count++;
method1();
}
public static void main(String[] args) {
//Stack Overflow
try{
method1();
}catch(Throwable e){
e.printStackTrace();
System.out.println(count);
}
}
ObjectMapper mapper = new ObjectMapper();
Sytem.out.printIn(mapper.writeValueAsString(d));
@JsonIgnore
private Dept dep t;
---------------------------
线程运行诊断
①CPU占用够多
定位
1.用top定位到哪个进程对cpu的占用过高
2.用ps H -eo pid,tid,%cpu | grep 进程id(用ps命令进一步定位是哪个线程引起的cpu占用过高)
3.jstack 进程id 查看哪个线程出现问题 进一步定位到问题代码的源码行号
这里的进程id是十进制的 列出的数据中的线程id为十六进制 需要转换核对进程
top CPU占用比例(定位到进程)
ps H -eo pid,tid,%cpu | grep 32655
②吃吃得不到结果 死锁
Found one Java-level deadlock;
---------------------------
本地方法栈
native修饰 本地方法接口 C C++实现
---------------------------
堆(线程共享区)
通过new关键字创建对象都会使用堆内存
特点:
线程是共享的
垃圾回收机制
---------------------------
堆内存溢出 outofmemoryError
VM option: -Xms256k 参数设置大小
堆内存诊断
1.jps工具 查看看当前系统中有哪些java进程
2.jmap工具 查看堆内存占用情况(某一时刻) jmap -heap 进程id
3.jconsole工具 图形界面的 多功能的检测工具,可以连续监测
4. 堆转储 dump
jps
jmap -heap 18756
方法区内存溢出
1.8之前 永久代
OutOfMemoryError PermGen space
-XX:MaxPermSize=8m
1.8之后 元空间
OutOfMemoryError Metaspace
-XX:MaxMetaspaceSize=8m
场景:spring mybatis
---------------------------
运行时常量池
就是一张表,虚拟机指令根据这张常量表找到要执行的类名、方法名、参数类型、字面量等信息
StringTable
javac 在编译期间的优化 结果已经在编译期确定为ab
String s4 = "a" + "b";
String s2 = "ab";
true
String s5 = s1 + s2; //new StringBuilder().append("a").append("b").toString() new String("ab")
字符串字面量也是【延迟】成为对象的
String x = "ab";
String s = new String("a") + new String("b");//new String("ab");
//堆 new String("a") new String("a") new String("a")
String s2 = s.intern();
// 1.8 将这个字符串对象尝试放在串池,如果有则并不会放入 会把串池中的对象返回
s.intern() == "ab" true
s2 == x; true
s == x; false
//1.6 拷贝一份 放入串池
String s = new String("a") + new String("b");//new String("ab");
String s2 = s.intern();
String x = "ab";
//1.6
s2 == x; true
s == x; false
//1.8
s2 == x; true
s == x; true
String s3 = "a" + "b"; //ab
String s4 = s1 + s2; // new String("ab")
---------------------------
常量池的位置
-XX:MaxPermSize=8m 设置永久代的最大内存大小值 1.6在永久代
1.8在 堆内存
---------------------------
字符串垃圾回收 (HashTable )
优化调优:
1.-XX:StringTableSize = 桶个数 字符串常量大量的系统 建议设置StringtableSize大一点 桶数 Hash分布 减少Hash冲突
2.考虑将字符串对象是否入池 节约堆内存的使用
---------------------------
直接内存
sync和lock区别
1、原始构成
sync 关键字属于JVM层次 monitorenter(底层通过monitor对象来完成,wait、notify等方法依赖于monitor对象只有在{}里面才有想过) monitorexit 正常退出 异常退出 双重保险
lock 具体类 是api层次的锁
2.使用方法
sync不需要用户去手动释放锁 monitorexit 退出
lock则需要用户去手动释放锁 反之出现死锁现象 lock() unlock() 配合try/finally 语句块来完成
3.等待是否可中断
sync不可中断 除非抛出异常
lock可中断 1.设置超时方法trylock(long timeout,TimeUnit unit)
2.lockInterruptibly()放代码块中,调用interrupt()
4.加锁是否公平
sync 非公平
lock 默认非公平 都可以
5.锁绑定多个条件Condition
sync没有
lock 精确唤醒
============
-Xms 设置初始堆内存 十六分之一
-Xmx 最大堆内存 四分之一
-Xmn 设置年轻代大小
-Xss 设置单个线程大小
jinfo -flag ThreadStackSize 17908
-XX:MetaspaceSize 设置元空间大小 避免OOM
-XX:PrintGCDetails 输出GC的详细日志信息
-XX:SurvivorRatio
设置新生代中eden和S0/S1空间的比例 默认 -XX:SurvivorRatio=8 8:1:1
值就是eden区的比例占多少,S0/S1相同
-XX:NewRatio
配置年轻代与老年代在堆结构的占比
-XX:NewRatio=2 新生代1老年代2 年轻代占整个堆的1/3
-XX:MaxTenuringThreshold
设置垃圾最大年龄 最大只能是15(默认)
-XX:+PrintCommandLineFlags
-XX:InitialHeapSize=131920000
-XX:MaxHeapSize=2110720000
-XX:+PrintCommandLineFlags
-XX:+UseCompressedClassPointers
-XX:+UseCompressedOops
-XX:-UseLargePagesIndividualAllocation
-XX:+UseParallelGC
-Xms10m -Xmx10m -XX:+PrintGCDetails
-XX:+PrintCommandLineFlags
=============
强引用 软引用 弱引用 虚引用
强引用:当内存不足 JVM开始垃圾回收对于强引用的对象,就算出现了OOM也不会对该对象进行回收(死都不收)
最常见的就是强引用
软引用:相对强引用弱化了一些引用 系统内存充足 不回收 内存不足 回收
SoftReference
JVM配置 故意产生大对象并配置小的内存 导致内存不顾偶 OOM 看软引用的回收情况
-Xms5m =Xmx5m -XX:+PrintGVDetails
try{
byte[] bytes = new byte[30*1024*1024];
}
弱引用:只要有GC就回收
WeakReference<Object> we = new WeakReference<>(o1);
场景:读取大量本地图片 如果每次读取图片都从硬盘读取则会严重影响性能 如果一次性全部加载到内存中有可能造成内存溢出
此时使用软引用可以解决这个问题
Map<String,SoftReference<Bitmap>> imageCatvh = new HashMap<String,SoftReference<Bitmap>>();
软引用 WeakHashMap
虚拟引用
和引用队列(ReferenceQueue)联合使用
get方法发放总是null
唯一目的:就是在这个对象被收集器回收的时候收到一个系统通知或者后续添加进一步处理
超过98%的时间用来做GC 并且回收了不到2%的堆内存
GC overhead limit exceeded
元空间并不在虚拟机当中 存在本地内存
堆内存充足 本地内存已经用光了
Direct buffer memory
ByteBuffer.allocateDirect() 分配os本地内存 不属于GC管个范围 由于不需要内存拷贝所以速度表极快
unable to create new native thread
java.lang.outOfMemoryError
linux默认线程数1024 ulimit -u
增加数量:专门增加一行 root行下面拷贝
永久代(java8后被元空间取代了)存放信息
虚拟机加载的类信息
常量池
静态变量
即时编译的代码
Metaspace内存溢出模仿:不断新建类往元空间罐 超过指定空间大小
=============
如何获得默认的垃圾回收器
java -XX:+PrintCommandLineFlags -version
-XX:UseParallelGC
-XX:+UseSerialGC
开启后会使用:Serial(Young区用) + SerialOld(Old区用)的收集器组合
表示:新生代和老年代都会使用串行回收收集器,新生代使用复制算法 老年代使用标记-整理算法
ParNew收集器其实就是Serial收集器新生代的并行多线程版本 只影响新生代的收集 不影响老年代
开启后会使用:ParNew(Young区用) + SerialOld(Old区用)的收集器组合
表示:新生代使用复制算法 老年代采用标记-整理算法
使用场景:配合老年代的CMS GC工作
-XX:+UseParallelGC
Parallel Scavenage收集器类似ParNew 也是一个新生代垃圾收集器 使用复制算法 并行的多线程垃圾收集器
一句话:串行收集器在新生代和老年代的并行化
可控制的吞吐量 高效的利用CPU的时间 多用于后台运算而不需要太多交互的任务
自适应调节策略 调节停顿时间动态
新生代使用复制算法 老年代使用标记-整理算法
--CMS
四步过程:
初始标记 (stop)
并发标记 和用户线程一起
重新标记
并发清除 和用户线程一起
优点:并发收集停顿短 缺点:CPU资源压力大 采用标记清除算法会导致大量碎片
必须在老年代堆内存用尽之前完成垃圾回收 否则CMS回收失败时,将触发担保机制,串行老年代收集器将会以STW的方式进行一次GC,从而造成较大停顿时间
=============
G1是一种服务器端的垃圾回收器 应用在多处理器和大容量内存环境中 在实现高吞吐量的同时 尽可能的满足垃圾收集器暂停时间的要求
像CMS收集器一样 能与应用程序线程并发执行
整理空闲空间更快
不需要牺牲大量的吞吐性能
不需要更大的Java Heap
与CMS:是一个整理内存过程的垃圾收集器 不会产生很多内存碎片
G1的Stop The World更可控 在停顿时间上添加了预测机制 用户可以指定期望停顿时间
1.充分利用多CPU、多核环境硬件优势 尽量缩短STW
2.整体上采用标记-整理算法 局部是通过复制算法,不会产生内存碎片
3.宏观上不分Y O 区 分为多个独立的内存子区域 可以近似理解为一个围棋的棋盘
4.其本身依然在小范围内要进行年轻代和老年代的区分 保留了新生代和老年代 也就是说依然会采用不同的GC方式来处理不同的区域
5.G1虽然也是分代收集器 但整个内存分区不存在物理上的年轻代和老年代的区别 也不需要完全独立的survivor堆做复制准备
G1只有逻辑上的分代概念 或者说每个分区都可能随G1的运行在不同代之间前后切换
原理:
区域化内存划片Region
整体编为了一些列不连续的内存区域 避免了全内存的GC 操作
核心思想是将整个堆内存区域分为大小相同的Region 在JVM启动时会自动设置这些子区域的大小
将新生代 老年代的 物理空间划分取消了
小区域的收集形成连续的内存碎片
初始标记 - 并发标记 - 最终标记 - 帅选回收
2048个区域
-XX:+UseG1GC -Xmx32g -XX:MaxGCPauseMills=100
=============
1.IDEA开发完微服务工程
2.maven进行clean package
3.要求微服务启动的时候 同时配置我们的JVM/GC参数
3.1 内
3.2 外 ===》重点
4.公式
java -server jvm的各种参数 -jar 第一步上面的jar、war包的名字
=============
do while 自旋锁
如果CAS失败 会一直进行尝试 一直不成功的话会给CPU带来很大的开销
循环时间长 开销很大
只能保证一个共享变量的原子操作
ABA问题(狸猫换太子)
时间差类会导致数据的变化
ABA问题的解决 AtomicReference 理解原子引用
int stamp = atomicStampedReference.getStamp();
atomicStampedReference.compareAndSet(101,100,stamp,stamp+1);
集合错误
故障现象 ConcurrentModificationException
导致原因
解决办法
1.new Vector<>();
2.Collections.synchronizedList(new ArrayList<>());
3.new CopyOnWriteArrayList(); new CopyOnWriteArraySet();
HashSet的底层是HashMap key就是值
HashMap -- 》 ConcurrentHashMap
栈管运行 堆管存储
=============
公平锁和非公平锁
1.线程的申请顺序 2.后来先得 优先级反转或者饥饿现象
ReentrantLock 默认非公平锁 非公平有点在于吞吐量比公平锁大 对于synchronized 也是非公平锁
可重入锁(递归锁):ReentrantLock、synchronized 线程可以进入如何一个它已经拥有的锁所同步的代码块
外层方法获得锁的时候 在进入到内层方法自动获得锁
最大的作用:防止死锁
自旋锁:尝试取锁的线程不会立即阻塞 而是采用循环的方式去尝试获取锁 减少线程上下问切换的消耗 缺点是循环会消耗CPU
打电话 不用一直等 先干活再看
CAS思想 do while
独占锁:该锁一次只能被一个线程所持有 ReentrantLock、synchronized
共享锁:该锁可被多个线程所持有
对ReentrantReadWriteLock 读锁共享 写锁独占锁
CountDownLatch countDownLatch = new CountDownLatch(6)
countDownLatch.countDown();
countDownLatch.await();
https://blog.csdn.net/tolcf/article/details/50925145
线程在countDown()之后,会继续执行自己的任务,而CyclicBarrier会在所有线程任务结束之后,才会进行后续任务
Semaphore semaphore = new Semaphore(3);
semaphore .acquire();
semaphore .release();
=============
阻塞队列:当阻塞队列是空的时候 从队列中获取元素的操作被阻塞
当阻塞队列是满的时候 从队列中添加元素的操作被阻塞
好处是我们不需要关心什么时候需要阻塞线程 什么时候需要唤醒线程
BlockingQueue接口
ArrayBlockingQueue: 由数组结构组成的有界阻塞队列
LinkedBlockingDeque: 由链表结构组成的有界(但大小默认值Integer>MAX_VALUE)阻塞队列.
PriorityBlockingQueue:支持优先级排序的无界阻塞队列.
DelayQueue: 使用优先级队列实现的延迟无界阻塞队列
SynchronousQueue:不存储元素的阻塞队列,也即是单个元素的队列.
抛出异常组 add remove element
特殊值 布尔值 offer 插入方法成功true失败false 移除方法poll 成功返回出队列的元素 队列没有返回null
还有个peek()方法
管网上 一直阻塞:put()方法 队列一直阻塞生产线程直到put数据or响应中断退出
take()方法 队列会一直阻塞消费者线程直到队列可用
超时offer("a",2L,TimeUnit.SECONDS) put(2L,TimeUnit.SECONDS)
线程池的作用:线程复用 控制最大并发数 管理线程
底层threadpoolExecutor
第四种获得多线程方式 线程池、
Executors.newFixedThreadPool(5); 执行长期的任务 性能好很多 Link
Executors.newSingleThreadPool(); 一个任务一个任务执行的场景 single
Executors.newCachedThreadPool(); 执行很多短期异步的小程序或者负载较轻的服务器 Link
应用场景:都没用过
七大参数:
核心线程数 最大线程数 多余的空闲线程存活时间 时间单位 阻塞队列 线程工厂(默认) 拒绝策略
拒绝策略:
AbortPolicy(默认) 直接抛出异常
CallerRunsPolicy 回退到调用前
DiscardOldestPolicy 抛弃队列等待最久的任务 然后把当前任务加入到队列中尝试再次提交当前任务
DiscardPolicy 直接丢弃任务 不予任何处理也不抛出异常
都实现了RejectedExecutionHandler接口
FixedThreadPool和SingleThreadPool 允许的请求队列长度为Integer.MAX_VALUE 可能会堆积大量的请求 导致OOM
CachedThreadPool 和ScheduledThreadPool 允许的线程数量为Integer.MAX_VALUE 可能会创建大量的线程 导致OOM
线程池不允许用Executors去创建 而是通过ThreadPoolExecutor的方式
availableprocessors 获得多核数
CPU密集型 核数 + 1 该任务需要大量的运算 而没有阻塞 CPU一直全速运行
IO密集型 CPU核数*2 任务不是一直在执行的
如果大部分线程都阻塞 需要多配制线程数 CPU核数/1-阻塞系数 0.8~0.9
-Xms 等价于 -XX:InitialHeapSize
-Xmx 等价于 -XX:MaxHeapSize
查看jvm盘点全部初始化参数
java -XX:+PrintFlagsInitial
最后修改过 具有:就是更改过的
java -XX:+PrintFlagsFinal
网友评论