Java基础

本文脑图来源
https://www.bilibili.com/read/cv6238451?spm_id_from=333.788.b_636f6d6d656e74.6#reply2963501648

Java 八股文

八股文

访问权限比较–public,protected,默认,private

权限同一个类同一个包子类不同包的子类不同包的非子类
public
protected
默认
private

Java中final关键字详解及实例

class Value {
	  int v;
	  public Value(int v) {
	    this.v = v;
	  }
}
public class FinalTest {
	  final int f1 = 1;
	  final int f2;
	  
	  public FinalTest() {
	    f2 = 2;
	  }
	  
	  public static void main(String[] args) {
		    final int value1 = 1;
		    // value1 = 4;
		    final double value2;
		    value2 = 2.0;
		    final Value value3 = new Value(1);
		    value3.v = 4;
	  }
}
  • main方法中被final修饰的数据,在给value1赋初始值之后,我们无法再对value1 的值进行修改,final关键字起到了常量的作用。
  • 从value2我们可以看到,final修饰的变量可以不在声明时赋值,即可以先声明,后赋值。
  • value3是一个引用变量,这里我们可以看到final修饰引用变量时,只是限定了引用变量的引用不可改变,即不能将value3再次引用另一个Value对象,但是引用的对象的值是可以改变的。

静态代码块、构造代码块、构造函数、普通代码块

静态代码块、构造代码块、构造函数、普通代码块

Java(静态变量、实例变量、局部变量)

  • 静态变量----线程不安全,类的所有实例共享同一个static变量,对象间共享值时使用
  • 实例变量----实例变量属于类对象的,也就是说,属于对象实例私有,在虚拟机的堆中分配。
  • 局部变量----线程安全,定义在方法内部的变量

继承中 子、父类的构造方法

JAVA继承中子父类的构造方法

1 不写构造方法,类中的第一行代码事实上有一个默认的无参构造(系统会隐式为你写好)
2 只写带参构造方法,相当于只有该带参构造方法(隐式的无参构造会被屏蔽无视掉,视为无效
3 若想同时拥有无参和带参构造,必须显式地写出无参和带参构造方法
4 在子类的构造方法(无论是无参和有参)中,方法中的第一行代码事实上都隐式地包含了父类的无参构造方法即: super();此时若写一个有参构造,super(xx)会把隐式的super() 屏蔽掉

java内部类有什么作用?

java内部类有什么作用?

详解匿名内部类

【重要】使用匿名内部类我们必须要继承一个父类或者实现一个接口

public abstract class Bird {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
    
    public abstract int fly();
}

public class Test {
    
    public void test(Bird bird){
        System.out.println(bird.getName() + "能够飞 " + bird.fly() + "米");
    }
    
    public static void main(String[] args) {
        Test test = new Test();
        test.test(new Bird() {
            
            public int fly() {
                return 10000;
            }
            
            public String getName() {
                return "大雁";
            }
        });
    }
}
------------------
// Output:
// 大雁能够飞 10000米


// 相当于
public class WildGoose extends Bird{
    public int fly() {
        return 10000;
    }
    
    public String getName() {
        return "大雁";
    }
}

WildGoose wildGoose = new WildGoose();
test.test(wildGoose);

枚举Enum

Java集合框架图

在这里插入图片描述

https://www.runoob.com/java/java-collections.html
在这里插入图片描述

数组和集合区别

数组集合
存储元素可以存储基本数据类型,引用数据类型,基本数据类型存储的是值,引用数据类型存储的是地址值只能存储引用数据类型(对象)集合中也可以存储基本数据类型,但是在存储的时候会自动装箱变成对象
长度长度是固定 声明数组必须指明长度长度的是可变

map

Java中Map的 entrySet() 详解以及用法(四种遍历map的方式)

HashMap

敖丙 HashMap

1 数组 + 链表 + 红黑树 组合构成的数据结构
put插入的时候会根据key的hash去计算一个index值
数组长度是有限的,在有限的长度里面我们使用哈希,哈希本身就存在概率性,就是”帅丙“和”丙帅“我们都去hash有一定的概率会一样,就像上面的情况我再次哈希”丙帅“极端情况也会hash到一个值上,那就形成了链表
2 java8之后,尾部插入
3 HashMap的扩容机制: 初始化长度16 为了实现均匀分布
数组容量是有限的,数据多次插入的,到达一定的数量就会进行扩容,也就是resize。什么时候resize呢?有两个因素:
Capacity:HashMap当前长度。
LoadFactor:负载因子,默认值0.75f

怎么扩容的呢?分为两步
扩容:创建一个新的Entry空数组,长度是原数组的2倍。
ReHash:遍历原Entry数组,把所有的Entry重新Hash到新数组。
为什么要重新Hash呢,直接复制过去不香么
因为长度扩大以后,Hash的规则也随之改变。
Hash的公式—> index = HashCode(Key) & (Length - 1)
原来长度(Length)是8你位运算出来的值是2 ,新的长度是16你位运算出来的值明显不一样了。

线程不安全
Java7在多线程操作HashMap时可能引起死循环,原因是扩容转移后前后链表顺序倒置,在转移过程中修改了原来链表中节点的引用关系。

Java8在同样的前提下并不会引起死循环,原因是扩容转移后前后链表顺序不变,保持之前节点的引用关系。

我们重写equals方法的时候需要重写hashCode方法
https://www.cnblogs.com/ouym/p/8963219.html

HashMap常见面试题:

HashMap的底层数据结构?

HashMap的存取原理?

Java7和Java8的区别?

为啥会线程不安全?

有什么线程安全的类代替么?

默认初始化大小是多少?为啥是这么多?为啥大小都是2的幂?

HashMap的扩容方式?负载因子是多少?为什是这么多?

HashMap的主要参数都有哪些?

HashMap是怎么处理hash碰撞的?

hash的计算规则?

接口

接口和类

Java 接口(interface)的用途和好处
https://blog.csdn.net/qq_19782019/article/details/80259836
https://blog.csdn.net/nvd11/article/details/41129935

接口, 就是抽象方法和常量值的集合 也有普通方法(有返回值的方法)
java接口可以多继承。interface C extends A, B {}是可以的。
interface 的定义跟java有点类似. 大概如下:

[public]  interace interface_name [extends <Superinterface_list>]{

       //public static final members

       //public abstract methods

}

一个类可以实现多个接口:class D implements A,B,C{}。但是一个类只能继承一个类,不能继承多个类:class B extends A{}。在继承类的同时,也可以继承接口:class E extends D implements A,B,C{}。

接口的使用原则如下:
1、接口必须要有子类,但此时一个子类可以使用implements关键字实现多个接口。
2、接口的子类(如果不是抽象类),那么必须要覆写接口中的全部抽象方法。
3、接口的对象可以利用子类对象的向上转型进行实例化。

抽象类和接口

众所周知,在实现类中实现接口时,必须全部重写接口的抽象方法,如

public interface MyInter{

  function abstract void A();

  function abstract voidB();

}

实现类中,必须全部重写A和B方法

public class MyClass implements MyInter{

  public void A(){}

  public void B(){}

}

如果不重写接口的方法可以吗?可以,但实现类一定要是一个抽象类

public abstract class MyClass implements MyInter{

  public void A(){} 

}

所以总结如下:

由普通的类来实现接口,必须将接口所有抽象方法重写

由抽象类来实现接口,则不必重写接口的方法。可以全部不重写或只重写一部分方法。

用的时候找到对应的实现了接口方法的抽象类的方法

Java 泛型

Java 反射

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;
对于任意一个对象,都能够调用它的任意一个方法和属性;
这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。

https://blog.csdn.net/qq_42537795/article/details/106017092

Java基础之—反射(非常重要)

先读这个图,然后读廖雪峰的https://www.liaoxuefeng.com/wiki/1252599548343744/1264799402020448

学习java应该如何理解反射? - CaryTseng的回答 - 知乎
在这里插入图片描述

类class(包括interface)的本质是数据类型(Type)
Class 对象 实例 class是Class的一个属性

知识点一

### 如何获取一个classClass实例?有三个方法:
// 方法1:如果知道一个class的完整类名,可以通过静态方法Class.forName()获取:
// 最常用
Class clazz = Class.forName("java.lang.String");
//创建此Class 对象所表示的类的一个新实例
Object o = clazz.newInstance(); //调用了String的无参数构造方法.

// 方法2:直接通过一个class的静态变量class获取:
Class clazz = String.class;

// 方法3:如果我们有一个实例变量,可以通过该实例变量提供的getClass()方法获取:
String s = "Hello";
Class clazz = s.getClass();



##### 注意:在运行期间,一个类,只有一个Class对象产生

知识点二

//Class类提供了以下几个方法来获取**字段**:
Field getField(name):根据字段名获取某个public的field(包括父类)
Field getDeclaredField(name):根据字段名获取当前类的某个field(不包括父类)
Field[] getFields():获取所有public的field(包括父类)
Field[] getDeclaredFields():获取当前类的所有field(不包括父类)

//Class类提供了以下几个方法来获取Method:
Method getMethod(name, Class...):获取某个publicMethod(包括父类)
Method getDeclaredMethod(name, Class...):获取当前类的某个Method(不包括父类)
Method[] getMethods():获取所有publicMethod(包括父类)
Method[] getDeclaredMethods():获取当前类的所有Method(不包括父类)

//通过Class实例获取Constructor的方法如下:
getConstructor(Class...):获取某个publicConstructorgetDeclaredConstructor(Class...):获取某个ConstructorgetConstructors():获取所有publicConstructorgetDeclaredConstructors():获取所有Constructor

https://blog.csdn.net/sinat_38259539/article/details/71799078
类加载过程
在这里插入图片描述

.getClass()方法与.class方法区别

Java中的getGenericSuperclass的基本用法

// 通过getGenericSuperclass方法可以获取当前对象的直接超类的Type,
// 使用该方法可以获取到泛型T的具体类型


getSuperclass:class java.util.HashMap
getGenericSuperclass:java.util.HashMap<java.lang.String, java.lang.Integer>
class java.lang.String
class java.lang.Integer
 
getSuperclass:class java.util.AbstractMap
getGenericSuperclass:java.util.AbstractMap<K, V>
K
V
// https://www.cnblogs.com/chenmingjun/p/9734239.html
// 父类
package com.itheima.mytest;

public class Person<T1, T2> {

}



// 子类
package com.itheima.mytest;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

public class Student extends Person<Integer, String> {

    @SuppressWarnings("rawtypes")
    public static void main(String[] args) {
        Student student = new Student();
        // getClass() 获得该类的类类型(即类型变量)
        Class clazz = student.getClass();
        System.out.println(clazz);
        // getSuperclass() 获得该类的父类
        System.out.println(clazz.getSuperclass());

        // getGenericSuperclass() 获得该类带有泛型的父类
        Type type = clazz.getGenericSuperclass();
        System.out.println(type);
        // Type是 Java 编程语言中所有类型的公共高级接口。它们包括原始类型、
        // 参数化类型、数组类型、类型变量和基本类型。

        // ParameterizedType 参数化类型,即泛型
        // 将Type转化为参数化类型(即泛型)
        ParameterizedType p = (ParameterizedType) type;

        // getActualTypeArguments() 获取参数化类型的数组,泛型可能有多个
        Type[] actualTypeArguments = p.getActualTypeArguments();

        // 将Type转化为类型变量(即Class)
        Class c1 = (Class) actualTypeArguments[0];
        Class c2 = (Class) actualTypeArguments[1];
        System.out.println(c1);
        System.out.println(c2);
    }
}

class TestReflect.Student
class TestReflect.Person
TestReflect.Person<java.lang.Integer, java.lang.String>
class java.lang.Integer
class java.lang.String

IO流

System.in是个字节流
InputStreamReader是个字符流和字节流之间的转换中介
BufferedReader是个字符流
整体意思就是用InputStreamReader这个中介把System.in这个字节流转换成字符流BufferedReader
这样输入的时候就可以不是一个一个字节读,而是一个一个字符读,再加上是个Buffer,效率会高很多。

BufferedReader reader= new BufferedReader(new InputStreamReader(System.in))解读

public Properties reloadProperties() {
		Properties props = new Properties();
		InputStreamReader inputStreamReader = null;
		BufferedReader bufferedReader = null;
		try {
			inputStreamReader = new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8);
			bufferedReader = new BufferedReader(inputStreamReader);
			props.load(bufferedReader);
			if (props.isEmpty()) {
				log.error("The {} file cannot be load", file.getPath());
			}
		} catch (FileNotFoundException e) {
			log.error("construct file input stream caused exception, {}", e.getMessage());
		} catch (IOException e) {
			log.error("load properties caused exception, {}", e.getMessage());
		} finally {
			try {
				if (null != bufferedReader) {
					bufferedReader.close();
				}
			} catch (IOException e) {
				log.error("close buffer reader failed, {}", e.toString());
			}

			try {
				if (null != inputStreamReader) {
					inputStreamReader.close();
				}
			} catch (IOException e) {
				log.error("close input stream reader failed, {}", e.toString());
			}
		}

		log.info("reload properties =" + props);
		return prop = props;
	}

Java多线程

多线程

创建一个新线程非常容易,我们需要实例化一个Thread实例,然后调用它的start()方法
方法一:从Thread派生一个自定义类,然后覆写run()方法
方法二:创建Thread实例时,传入一个Runnable实例

线程池

在这里插入图片描述
在这里插入图片描述
https://www.cnblogs.com/hsug/p/13303018.html @EnableAsync @Async
https://www.cnblogs.com/uniqueDong/p/10944632.html

线程池的几个灵魂拷问?
api文档
https://www.matools.com/api/java8
https://movie.blog.csdn.net/article/details/82904688
https://www.liaoxuefeng.com/wiki/1252599548343744/1306581130018849
https://www.cnblogs.com/Cubemen/p/10818895.html


Executors 线程池执行器类 :
1. newCachedThreadPool(); 缓存数量线程池.
2. newFixedThreadPool(); 固定数量线程池.
3. newSingleThreadExecutor(); 单个可复用线程池对象.
 
ExecutorService 执行器服务类 :
1. submit(Runnable task); 向线程池对象提交任务.
2. shutdown(); 关闭线程池对象.

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

int corePoolSize = 10;  //核心线程数
int maximumPoolSize = 15;  //最大线程数
long keepAliveTime = 10;  //线程空闲时间
TimeUnit unit = TimeUnit.SECONDS;
LinkedBlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>();
ExecutorService executor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit,
        workQueue);
executor.execute(funcxxx);
executor.shutdown();
public class ThreadPool {

	public final static String MY_QUEUE_THREAD_POOL_NAME = "myTaskExecutor";
	
	@Bean(MY_QUEUE_THREAD_POOL_NAME)
	public Executor getPacketInAsyncExecutor() {
		ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
		executor.setCorePoolSize(notifyThreadPoolProperties.getCoreSize());
		executor.setMaxPoolSize(notifyThreadPoolProperties.getMaxSize());
		executor.setQueueCapacity(notifyThreadPoolProperties.getQueueSize());
		executor.setKeepAliveSeconds(notifyThreadPoolProperties.getTimeout());
		executor.setThreadNamePrefix("My-task-");
		// 拒绝策略
		// 如果任务被拒绝了,则由调用线程(提交任务的线程)直接执行此任务
		executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
		executor.initialize();
		return executor;
	}

多线程执行同一任务,所有线程返回值后执行下一步操作测试(可用于多线程查询)

https://juejin.cn/post/6989201934699741191 CountDownLatch的使用场景
https://blog.csdn.net/mhqyr422/article/details/115306773

package com.lx.learn.testConcurrent;


import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;


/**
 * @Description:多线程执行同一任务,所有线程返回值后执行下一步操作测试(可用于多线程查询)
 * @Auther:
 * @Date:
 */
public class MultiThread {
    private static ExecutorService threadPool = null;

    static {
        threadPool = Executors.newFixedThreadPool(20);
    }

    public static void main(String[] args) {
        try {
//            getNumArg(1);
//            getNumArg(2);
            getForNum();
            System.out.println("方法执行完毕");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * @Description: 简单多线程执行(参数利用)
     * @Param: []
     * @return: void
     * @Author: MaoHQ
     * @Date: 2021/3/25
     */
    public static void getNumArg(int num) throws InterruptedException, ExecutionException {
        List<String> tmplist = new ArrayList<String>();
        CountDownLatch latch = new CountDownLatch(2);
        Future<Integer> lnt = threadPool.submit(new TestThread1(1, tmplist, latch));
        Future<Integer> ykt = threadPool.submit(new TestThread2(2, tmplist, latch));
        //等待线程均完成
        latch.await();
        //获取线程返回值
        Integer i1 = lnt.get();
        Integer i2 = ykt.get();
        //线程均执行完后,继续执行
        System.out.println(i1 + i2 + "数据:" + num);
        for (String string : tmplist) {
            System.out.println(string + "数据:" + num);
        }

    }

    /**
     * @Description: 多线程执行
     * @Param: []
     * @return: void
     * @Author: MaoHQ
     * @Date: 2021/3/25
     */
    public static void getNum() throws InterruptedException, ExecutionException {
        Future<Integer> t1 = threadPool.submit(new TestThread1(1));
        Future<Integer> t2 = threadPool.submit(new TestThread2(2));
        //获取线程返回值
        Integer i1 = t1.get();
        Integer i2 = t2.get();
        //线程均执行完后,继续执行
        System.out.println(i1 + i2);
    }

    /**
     * @Description: 多线程执行(循环)
     * @Param: []
     * @return: void
     * @Author: MaoHQ
     * @Date: 2021/3/29
     */
    public static void getForNum() throws InterruptedException, ExecutionException {
        int total = 5;
        List<Future<Integer>> allFuture = new ArrayList();
        CountDownLatch latch = new CountDownLatch(total);
        for (int i = 0; i < total; i++) {
            Future<Integer> tmpData = threadPool.submit(new TestThread1(1, latch));
            allFuture.add(tmpData);
        }
        latch.await();
        for (Future future : allFuture) {
            Integer num = (Integer) future.get();
            System.out.println(num);
        }

    }

    /***
     * @Description: 线程2(执行中有睡眠)
     * @Param:
     * @return:
     * @Author: MaoHQ
     * @Date: 2021/3/25
     */
    private static class TestThread2 implements Callable<Integer> {
        private int num;
        private List<String> tmplist;
        private CountDownLatch latch;

        public TestThread2(int num) {
            this.num = num;
        }

        public TestThread2(int num, List<String> tmplist) {
            this.num = num;
            this.tmplist = tmplist;
        }

        public TestThread2(int num, List<String> tmplist, CountDownLatch latch) {
            this.num = num;
            this.tmplist = tmplist;
            this.latch = latch;
        }

        @Override
        public Integer call() throws Exception {
            try {
                System.out.println("正在运行2");
                Thread.sleep(5000);
                if (tmplist != null)
                    tmplist.add("a");
                return num;
            } finally {
                latch.countDown();
            }
        }

    }

    /***
     * @Description: 线程1(执行中无睡眠)
     * @Param:
     * @return:
     * @Author: MaoHQ
     * @Date: 2021/3/25
     */
    private static class TestThread1 implements Callable<Integer> {
        private int num;
        private List<String> tmplist;
        private CountDownLatch latch;

        public TestThread1(int num, List<String> tmplist) {
            this.num = num;
            this.tmplist = tmplist;
        }

        public TestThread1(int num) {
            this.num = num;
        }

        public TestThread1(int num, List<String> tmplist, CountDownLatch latch) {
            this.num = num;
            this.tmplist = tmplist;
            this.latch = latch;
        }

        public TestThread1(int num, CountDownLatch latch) {
            this.num = num;
            this.latch = latch;
        }

        @Override
        public Integer call() throws Exception {
            try {
                if (tmplist != null)
                    tmplist.add("b");
                System.out.println("正在运行");
                return num;
            } finally {
                //线程执行数-1
                latch.countDown();
            }
        }

    }

}

Thread.start() ,它是怎么让线程启动的呢?

Thread.start() ,它是怎么让线程启动的呢?

synchronized 与 lock

在这里插入图片描述

Java锁

在这里插入图片描述
更多锁 阅读自己的java 锁以及分布式锁文章

可重入锁

演示
1.同一个锁 未释放,其它人拿不到
2.可重入锁:step1:create
step2: acquire 几次就要release几次,否则别人拿不到

 public void getReentrantLock() {
        System.out.println(CommonUtil.getCurrentThreadTrace());
        InterProcessMutex globalLock = ZkFunProvider.createReentrantLock(LockType.VPP_MANAGE_IP_LOCK, "1234");
        try {
            globalLock.acquire(500, TimeUnit.MILLISECONDS);
        } catch (Exception e) {
            System.out.println(CommonUtil.getCurrentThreadTrace() + "first acquire caused exception.");
        }

        InterProcessMutex lock1 = ZkFunProvider.getReentrantLock(LockType.VPP_MANAGE_IP_LOCK, "1234", 500);
        if (null == lock1) {
            System.out.println("get lock1 failed.");
        } else {
            System.out.println("get lock1 succeed.");
        }

        try {
            globalLock.acquire(500, TimeUnit.MILLISECONDS);
        } catch (Exception e) {
            System.out.println(CommonUtil.getCurrentThreadTrace() + "second acquire caused exception.");
        }

        ZkFunProvider.releaseLock(globalLock);
        System.out.println("release first time.");

        lock1 = ZkFunProvider.getReentrantLock(LockType.VPP_MANAGE_IP_LOCK, "1234", 500);
        if (null == lock1) {
            System.out.println("get lock1 failed.");
        } else {
            System.out.println("get lock1 succeed.");
        }

        ZkFunProvider.releaseLock(globalLock);
        System.out.println("release second time.");

        lock1 = ZkFunProvider.getReentrantLock(LockType.VPP_MANAGE_IP_LOCK, "1234", 500);
        if (null == lock1) {
            System.out.println("get lock1 failed.");
        } else {
            System.out.println("get lock1 succeed.");
        }
    }
get lock1 failed.

release first time.

get lock1 failed.

release second time.

get lock1 succeed.

不可重入锁

ZkFunProvider.getReentrantLock 这个用法其实是一个不可重入锁的用法 别被名字误导

JVM 垃圾回收

在这里插入图片描述

在这里插入图片描述

JVM内存区域

在这里插入图片描述
JVM内存模型:虚拟机栈与本地方法栈

本地方法栈:
jvm的native方法:一个Native Method就是一个java调用非java代码的接口

本地内存不懂 ??

敖丙 垃圾回收

https://www.bilibili.com/video/BV1HQ4y1P7hE
38:58 JVM 面试

— JVM内存区域
— 如何识别垃圾
--------- 引用计数 循环引用问题而不用
--------- 可达性算法 GC Root到底是什么东西呢,哪些对象可以作为 GC Root 呢,有以下几类
1 虚拟机栈(栈帧中的本地变量表)中引用的对象
2 方法区中类静态属性引用的对象
3 方法区中常量引用的对象
4 本地方法栈中 JNI(即一般说的Native 方法)引用的对象
—垃圾回收算法 4种
---------标记清除算法
---------标记复制算法
---------标记整理法
---------分代收集算法:新生代和老生代(Java8以前还有个永久代),默认比例为 1 : 2,新生代又分为 Eden 区, from Survivor 区(简称S0),to Survivor 区(简称 S1),三者的比例为 8: 1 : 1,这样就可以根据新老生代的特点选择最合适的垃圾回收算法,我们把新生代发生的 GC 称为 Young GC(也叫 Minor GC),老年代发生的 GC 称为 Old GC(也称为 Full GC)。
1、对象在新生代的分配与回收
2、对象何时晋升老年代 (阈值 大对象 )
3、Stop The World 即在 GC(minor GC 或 Full GC)期间,只有垃圾回收器线程在工作,其他工作线程则被挂起。
—垃圾收集器种类
---------CMS Concurrent Mark-Sweep 核心是支持工作线程和GC线程并发,工作在老年代 主要使用标记清除法 初始标记和重新标记两个阶段会发生 STW,造成用户线程挂起
主要有以下四个步骤
初始标记
并发标记
重新标记
并发清除
---------G1 Garbage First 会发生 STW,造成用户线程挂起 STW可控制
— OOM排查过程
— CPU飙高排查过程 top命令

在这里插入图片描述

CMS Concurrent Mark-Sweep

在这里插入图片描述
在这里插入图片描述

G1

在这里插入图片描述

在这里插入图片描述

FullGC问题排查

FullGC问题排查

原创 | Java堆内存是线程共享的!面试官:你确定吗?
在这里插入图片描述
GC (Allocation Failure) 那些事

JVM

如何三天啃下《深入JVM虚拟机第三版》

还在学JVM?我都帮你总结好了(附脑图)

如何啃下JVM这座大山,完结撒花(完结篇)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值