Java常见问题总结二

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 softReference = new SoftReference<>(o1); // 创建一个软引用
System.out.println(softReference.get()); //获取引用 
  

弱引用:只要一进行垃圾回收,不管JVM内存是否充足都会进行回收

Object o1 = new Object();
WeakReference weakReference = new WeakReference<>(o1); // 弱引用
System.out.println(weakReference.get());  // 获取引用 
  

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 queue = new ReferenceQueue<>();
PhantomReference reference = new PhantomReference<>(o1,queue); // 创建一个虚引用,并和引用队列进行绑定
System.out.println(reference.get()); // 虚引用始终为null 
  

引用队列:是用来配合引用工作的,没有这个队列引用也可以正常工作,创建引用时可以指定引用队列,当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并不在虚拟机中存在,主要存放了常量池,静态变量,虚拟机加载的类信息,即时编译后的代码

 

你可能感兴趣的:(Java)