1、阻塞队列
是一个队列,当阻塞队列是空的时候从队列中取元素的操作会被阻塞,当队列是满的时候从队列中放元素就会被阻塞,共有7种阻塞队列。
ArrayBlockingQueue:由数组组成的有界阻塞队列
LinkedBlockingQueue:由链表组成的有界阻塞队列,默认大小是Integer.max_value。
PriorityBlockingQueue:按照优先级的无界阻塞队列
DelayQueue:按照优先级实现的延迟的无界阻塞队列
SynchronousQueue:不存储元素的阻塞队列,即单个元素的阻塞队列
LinkedTransferQueue:由链表组成的无界阻塞队列
LinkedBlockingDeque:由链表组成的双向阻塞队列
阻塞队列的使用
public static void main(String[] args) throws InterruptedException {
BlockingQueue queue = new ArrayBlockingQueue<>(3); // 创建一个阻塞队列
/*第一种方式**/
queue.add("a"); // 添加元素,溢出报错
System.out.println(queue.element()); // 获取队首元素
queue.remove("a");// 删除元素
/*第二种方式**/
queue.offer("a"); // 添加元素,溢出返回false
queue.offer("a",2L,TimeUnit.SECONDS); // 可以添加时间
System.out.println(queue.peek()); // 获取队首元素
queue.poll(); // 删除元素
/*第三种方式**/
queue.put("a"); //添加元素,当队列满的时候就阻塞
queue.take(); //移除元素,当队列为空就阻塞
}
2、Condition
package exc;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
class ShareSource{
private int number = 1;
private ReentrantLock lock = new ReentrantLock();
private Condition c1 = lock.newCondition(); // 创建一个condition
private Condition c2 = lock.newCondition();
public void print5() throws InterruptedException {
lock.lock();
while (number != 1){
c1.await(); // c1进行阻塞
}
System.out.println("111");
number = 5;
// 精准唤醒c2,c2.singnalAll()就是唤醒全部
c2.signal();
lock.unlock();
}
public void print15() throws InterruptedException {
lock.lock();
while (number != 5){
c2.await();
}
System.out.println("222");
number = 1;
// 唤醒c1
c1.signal();
lock.unlock();
}
}
public class Sync {
public static void main(String[] args) {
ShareSource s = new ShareSource();
new Thread(() -> {
for(int i=0;i<=4;i++){
try {
s.print5();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"aa").start();
new Thread(() -> {
for(int i=0;i<=4;i++){
try {
s.print15();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"bb").start();
}
}
3、GC roots
是一组必须活跃的引用,基本思路就是以GC roots引用对象作为起点进行通过引用关系遍历对象图,可以被遍历到的就为存活,否则就会判定为死亡。
哪些是GC对象?
虚拟机栈中引用的对象。
方法区中的类静态属性引用的对象。
方法区中的常量引用的对象。
本地方法栈(Native方法)中引用的对象。
4、JVM的参数
标配参数:
-version 查看版本号
-help 查看帮助
X参数:
-Xint 解释执行模式
-Xcomp 第一次使用就编译成本地代码
-Xmixed 混合模式
XX参数之布尔类型参数:
总结为一个公式模板:-XX:+/- 属性,+/-代表开启或关闭。
例:-XX:+PrintGCDetails
要想查看是否开启属性值就需要先用到Java相关的工具。
首先通过jps工具,在输入jps -l命令以后就会看到正在执行Java文件的线程编号。
然后再通过jstack工具,输入jstack 线程号 就可以进行解析查看是否是死锁死循环等问题。
最后还可以通过jinfo工具进行参数的查看,输入jinfo -flag 属性名称 线程号 来查看是否开启。
XX参数之KV型参数:
总结为一个公式模板:-XX: 属性=值。
例:-XX:MaxTenuringThreshold=12
常用JVM参数
查看JVM配置的初始默认值:java -XX:+PrintFlagsInitial [-version (具体参数)]
查看JVM配置的更新修改后的值:Java -XX:+PrintFlagsFinal [-version (具体参数)]
--uintx CodeCacheExpansionSize = 65536(属于kv型)
--bool ClassUnloading = true(属于布尔型)
--ClassUnloading := true 代表修改以后的值
-Xms参数:初始大小内存,默认是物理内存的1/64 -Xms 10m等价于-XX:InitialHeapSize=10m
-Xmx参数:最大分配内存,默认为物理内存的1/4 -Xmx 10m等价于 -XX:MaxHeapSize=10m
-Xss参数:设置单个线程栈的大小,默认是512k--1024k -Xss 10m等价于-XX:ThreadStackSize=10m
--当用jinfo查询ThreadStackSize时会出现值为0的情况,这是代表用的系统默认值
-Xmn参数:设置年轻代的大小
-XX:MetaspaceSize参数:设置元空间的大小。
-- 元空间和永久代的区别:元空间并不在虚拟机中,而是使用本地内存,也就是说只受本地内存限制
-- 然而还会出现OOM的异常是因为,元空间只是用了本地内存的一部分而不是全部
-XX:SurvivorRatio参数:设置新生代中Eden的空间比例。
-- -XX:SurvivorRatio=8代表 Eden:s0:s1 = 8:1:1
-XX:NewRatio参数:配置年轻代和老年代在堆结构的占比。
-- -XX:NewRatio=3代表 新生代:老年代=1:3
-XX:MaxTenuringThreshold参数:设置垃圾最大年龄。
GC垃圾回收日志
当添加jvm参数 -XX:PrintGCDetails 在触发GC的情况下会出现以下日志
[GC [DefNew: 2242K->0K(2368K), 0.0018814 secs] 2242K->2241K(7680K), 0.0019172 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
GC代表是哪一种类型的GC
DefNew: 2242K->0K(2368K)。2242k代表yongGC前新生代内存占用 0k代表yongGC后新生代的占用 (2368k)代表新生代总共大小
2242K->2241K(7680K) 2242k代表yongGC前JVM堆内存的占用,2241k代表yongGC后JVM堆的内存使用情况,(7680k)JVM堆的总大小
总结为一个公式:名称:GC前占用内存->GC后占用内存
5、Java中的引用(强软弱虚)
强引用:当内存不足时,JVM开始垃圾回收,对于强引用的对象,就算出现OOM的问题也不会进行对象回收,该对象以后永远不会使用JVM也不会回收,因此强引用是造成Java内存泄漏的主要原因之一。
HelloGc helloGc = new HelloGc(); 这就是一个强引用
软引用:对于软引用对象来说当系统内存充足时不会进行回收,当内存不足时就会被回收。软引用通常在对内存敏感的程序中,内存够用就保留,不够用就回收。
Object o1 = new Object();
SoftReference
弱引用:只要一进行垃圾回收,不管JVM内存是否充足都会进行回收
Object o1 = new Object();
WeakReference
weakhashMap:并不是一个可并发操作的hashmap
WeakHashMap hashMap = new WeakHashMap<>(); // 创建一个key为弱引用的hashmap
String value = "WeakHashMao";
Integer key = new Integer(1);
hashMap.put(key, value);
key = null; //将key置位null
System.gc(); //这里模拟一次GC过程,这时会进行回收
System.out.println(hashMap + "\t " + hashMap.size()); // 这里的hashmap中的元素为空,但是结果有可能不为0,这个是由于指令重排导致的
虚引用:顾名思义,形同虚设,如果一个对象仅持有虚引用,那么它将和没有任何引用一样,任何时候都有可能被回收
Object o1 = new Object();
ReferenceQueue
引用队列:是用来配合引用工作的,没有这个队列引用也可以正常工作,创建引用时可以指定引用队列,当GC释放对象时会将引用加入到引用队列。
Object o1 = new Object();
ReferenceQueue queue = new ReferenceQueue<>();
WeakReference weakReference = new WeakReference<>(o1,queue); // 将引用和队列进行绑定,当被GC以后就会放到定义好的队列中
System.out.println(queue.poll()); // 在GC之前就是null
o1 = null;
System.gc();
Thread.sleep(3000);
System.out.println(queue.poll()); // 获取引用队列里的元素
6、OOM常见问题
java.lang.StackOverflowError(栈溢出错误)
-- 函数调用栈太深了,代码中是否有循环调用方法而无法退出的情况,例如递归操作。
java.lang.OutOfMemoryError:java heap space(堆溢出)
-- 内存中加载数据量过大,或者强引用太多导致JVM无法进行回收
java.lang.OutOfMemoryError:GC overhead limit exceeded
--当GC回收的时间太长的时候就会抛出这个异常,过长的定义就是当超过98%的时间全部来做GC,并且回收了
--不到2%的内存极端条件下就会抛出
java.lang.OutOfMemoryError:Direct buffer memory(直接缓存溢出)
--通过allocate进行内存分配是分配JVM内存堆,属于GC的管辖范围,拷贝的速度慢
--通过allocateDirect,直接分配到本地的内存,不属于GC的范围所以拷贝速度快
java.lang.OutOfMemoryError:unable to create new native thread(创建线程到达一定数目)
-- 当一个进程创建多个线程,超过了系统的承载量的极限
-- 解决方法,降低应用程序的创建线程数量
-- 对 vim /etc/security/limits.d/90-nproc.conf配置文件进行修改
java.lang.OutOfMemoryError:MetaSpace(元空间的溢出)
--MetaSpace并不在虚拟机中存在,主要存放了常量池,静态变量,虚拟机加载的类信息,即时编译后的代码