[ From B station video : https://www.bilibili.com/video/BV1BJ41177cp?p=154 ]
[강, 약, 약한 인용 : 더 부분적인 고주파 인터뷰 질문입니다. 야. 】
[테스트 결과 : System.gc () 메서드를 호출 할 때 SystemGCTes 클래스에 의해 재정의 된 finaliize 메서드가 때때로 호출되고 때때로 호출되지 않는 것으로 나타났습니다. 이는 System.gc () 메서드에 이러한 면책 조항이 있음을 보여줍니다. 가비지 수집기가 호출된다는 보장은 없습니다. 】
//测试不同情况下的GC。结果很妙,可以亲自测试下。-XX:PrintGCDetails
public class LocalVarGC {
public void localvarGC1() {
byte[] buffer = new byte[10 * 1024 * 1024];//10MB
System.gc();
}
public void localvarGC2() {
byte[] buffer = new byte[10 * 1024 * 1024];
buffer = null;
System.gc();
}
public void localvarGC3() {
{
byte[] buffer = new byte[10 * 1024 * 1024];
}
System.gc();
}
public void localvarGC4() {
{
byte[] buffer = new byte[10 * 1024 * 1024];
}
int value = 10;
System.gc();
}
public void localvarGC5() {
localvarGC1();
System.gc();
}
public static void main(String[] args) {
LocalVarGC local = new LocalVarGC();
local.localvarGC5();//这里改变为不同的方法调用
}
}
[GC의 반복적 인 개발로 정상적인 상황에서 응용 프로그램이 차지하는 메모리가 매우 빠르게 증가하여 가비지 수집이 메모리 소비 속도를 따라 가지 않으면 OOM이 발생하기 쉽지 않습니다. OOM보고 시나리오는 다음과 같습니다. 새 개체에 대한 메모리를 할당하려고하지만 JVM에 사용 가능한 메모리가 충분하지 않고 가비지 수집 후에 사용 가능한 메모리를 충분히 제공 할 수 없습니다. 이때 OOM 오류가보고됩니다. 】
[힙, 스택, 메소드 영역 등과 같은 어떤 종류의 메모리 공간 분할은 가상 메모리 라고하는 Java 가상 머신 레벨의 메모리 분할입니다 . 우리의 애플리케이션은 모두 자바 가상 머신에서 실행되고 있습니다. 】
[인터뷰 질문 : 메모리 누수 사례 : (1) 싱글 톤 모드 (2) 클로즈를 제공하는 일부 리소스가 닫히지 않아 리소스 누수 발생]
[ 메모리 누수 란 무엇입니까? 메모리 누수는 일부 Java 객체가 더 이상 응용 프로그램에서 사용되지 않지만 응용 프로그램에서 여전히 사용중인 일부 다른 Java 참조가 여전히 참조 관계를 유지하기 때문입니다. 자바 객체는 더 이상 사용되지 않지만 GCRoots에서 객체로의 참조 체인이 남아있어 가비지 수집기가 객체를 회수 할 수 없게되어 메모리 누수가 발생합니다. 메모리 누수의 예를들 수 있습니까? (1) 참조 카운팅 알고리즘에서 순환 참조의 경우가 메모리 누수의 예입니다. 그러나 Java에서 참조 계산 알고리즘은 가비지 객체를 식별하는 데 사용되지 않지만 도달 가능성 분석 알고리즘은 도달 가능한 객체를 식별하는 데 사용됩니다. 따라서, 전술 한 순환 참조 예는 참조 계수 알고리즘을 전제로하는 전제를 가져야한다. (2) 도달 가능성 분석 알고리즘의 맥락에서 메모리 누수의 예는 다음과 같습니다. (2.1) 예를 들어 싱글 톤 모드에서 싱글 톤 의 수명주기는 애플리케이션만큼 길기 때문에 싱글 톤에서 외부 객체를 보유하는 경우 참조되는 경우이 외부 개체는 재활용 할 수 없으며 메모리 누수가 발생합니다. (2.2) 예를 들어, close () 메서드를 제공하는 일부 리소스는 제때 닫히지 않고 IO 연결, 데이터베이스 연결 등과 같은 메모리 누수를 유발합니다 . 이러한 리소스는 사용 된 후 제 시간에 해제되어야합니다. 그렇지 않으면 메모리 누수가 발생합니다. . 】
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
//用来感受下STW事件的存在!
public class StopTheWorldDemo {
public static class WorkThread extends Thread {
List<byte[]> list = new ArrayList<byte[]>();
public void run() {
try {
while (true) {
for(int i = 0;i < 1000;i++){
byte[] buffer = new byte[1024];
list.add(buffer);
}
if(list.size() > 10000){
list.clear();
System.gc(); //会触发full gc,进而会出现STW事件
}
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
public static class PrintThread extends Thread {
public final long startTime = System.currentTimeMillis();
public void run() {
try {
while (true) {
// 每秒打印时间信息
long t = System.currentTimeMillis() - startTime;
System.out.println(t / 1000 + "." + t % 1000);
Thread.sleep(1000);
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
public static void main(String[] args) {
WorkThread w = new WorkThread();
PrintThread p = new PrintThread();
w.start(); //两种测试场景:(1)注释掉测试。(2)不注释掉测试。
p.start();
}
}
[Garbage Collection Thread 및 User Thread : 사용자 스레드는 어느 시점에서든 가비지 수집 스레드가 가비지 수집 스레드를 중지하고 수집하도록 할 수 있으며 특정 위치에서만 사용자 스레드를 일시적으로 중지하고 GC로 이동할 수 있습니다. 이러한 특정 위치는 "안전 지점" 입니다. 예 : 예를 들어 고속도로에서 운전할 때 고속도로의 어떤 위치에서도 마음대로 정지 할 수 없습니다. 서비스 영역에서만 정지 및 휴식이 허용됩니다. 이러한 서비스 영역은 안전 지점의 존재와 유사합니다 . 】
[인터뷰 질문 : GC가 발생했을 때 모든 쓰레드가 가장 가까운 안전한 지점에서 멈췄는지 확인하는 방법? 】
[참고 :이 장에서 언급 한 4 개의 strong, weak, weak 참조는 모두 오브젝트에 도달 할 수있을 때 설명되며 오브젝트에 도달 할 수없는 경우 직접 재활용됩니다. 】
[참고 :이 장에서 언급 한 4 개의 strong, weak, weak 참조는 모두 오브젝트에 도달 할 수있을 때 설명되며 오브젝트에 도달 할 수없는 경우 직접 재활용됩니다. 】
[첫 번째 가비지 수집 : 터치 할 수없는 자바 객체 (또는 도달 할 수없는 자바 객체)가 재활용되는 프로세스를 나타냅니다. OOM은 "비 강력한 참조"와는 아무 관련이 없습니다. OOM에 대해 "강력하지 않은 참조"가 재활용되고 제가 재활용되고 JVM이 여전히 OOM으로 나타나기 때문에이 OOM은 나와 관계가 있습니다. 】
import java.lang.ref.SoftReference;
//软引用的测试:内存不足即回收。-Xms10m -Xmx10m -XX:+PrintGCDetails
public class SoftReferenceTest {
public static class User {
public User(int id, String name) {
this.id = id;
this.name = name;
}
public int id;
public String name;
@Override
public String toString() {
return "[id=" + id + ", name=" + name + "] ";
}
}
public static void main(String[] args) {
//创建对象,建立软引用
// SoftReference<User> userSoftRef = new SoftReference<User>(new User(1, "songhk"));
//上面的一行代码,等价于如下的三行代码
User u1 = new User(1,"songhk");
SoftReference<User> userSoftRef = new SoftReference<User>(u1);
u1 = null;//一定要有的代码:取消强引用
//从软引用中重新获得强引用对象
System.out.println(userSoftRef.get());
System.gc();
System.out.println("After GC:");
// //垃圾回收之后获得软引用中的对象
System.out.println(userSoftRef.get());//由于堆空间内存足够,所有不会回收软引用的可达对象。
//
try {
//让系统认为内存资源紧张、不够
// byte[] b = new byte[1024 * 1024 * 7];//让系统认为内存资源不够
byte[] b = new byte[1024 * 7168 - 635 * 1024];//让系统认为内存资源紧张
} catch (Throwable e) {
e.printStackTrace();
} finally {
//再次从软引用中获取数据
System.out.println(userSoftRef.get());//在报OOM之前,垃圾回收器会回收软引用的可达对象。
}
}
}
[참고 :이 장에서 언급 한 4 개의 strong, weak, weak 참조는 모두 오브젝트에 도달 할 수있을 때 설명되며 오브젝트에 도달 할 수없는 경우 직접 재활용됩니다. 】
import java.lang.ref.WeakReference;
//弱引用的测试:发现即回收
public class WeakReferenceTest {
public static class User {
public User(int id, String name) {
this.id = id;
this.name = name;
}
public int id;
public String name;
@Override
public String toString() {
return "[id=" + id + ", name=" + name + "] ";
}
}
public static void main(String[] args) {
//构造了弱引用
WeakReference<User> userWeakRef = new WeakReference<User>(new User(1, "songhk"));
//从弱引用中重新获取对象
System.out.println(userWeakRef.get());
System.gc();
// 不管当前内存空间足够与否,都会回收它的内存
System.out.println("After GC:");
//重新尝试从弱引用中获取对象
System.out.println(userWeakRef.get());
}
}
[참고 :이 장에서 언급 한 4 개의 strong, weak, weak 참조는 모두 오브젝트에 도달 할 수있을 때 설명되며 오브젝트에 도달 할 수없는 경우 직접 재활용됩니다. 】
import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
//虚引用的测试:对垃圾回收没有任何影响。
//给对象设置一个虚引用关联关系的唯一目的:就是在对象垃圾回收器回收之时收到一个系统通知。
public class PhantomReferenceTest {
public static PhantomReferenceTest obj;//声明一个当前类的对象obj
static ReferenceQueue<PhantomReferenceTest> phantomQueue = null;//引用队列
public static class CheckRefQueue extends Thread {
@Override
public void run() {
while (true) {
if (phantomQueue != null) {
PhantomReference<PhantomReferenceTest> objt = null;
try {
objt = (PhantomReference<PhantomReferenceTest>) phantomQueue.remove();
} catch (InterruptedException e) {
e.printStackTrace();
}
if (objt != null) {
System.out.println("追踪垃圾回收过程:PhantomReferenceTest实例被GC了");
}
}
}
}
}
@Override
protected void finalize() throws Throwable { //finalize()方法只能被调用一次!
super.finalize();
System.out.println("调用当前类的finalize()方法");
obj = this; //使当前对象复活
}
public static void main(String[] args) {
Thread t = new CheckRefQueue();
t.setDaemon(true);//设置为守护线程:当程序中没有非守护线程时,守护线程也就执行结束。
t.start();
phantomQueue = new ReferenceQueue<PhantomReferenceTest>();
obj = new PhantomReferenceTest(); //obj是个强引用
//构造了 PhantomReferenceTest 对象的虚引用,并指定了引用队列
//使得obj这个强引用关联一个虚引用phantomRef
PhantomReference<PhantomReferenceTest> phantomRef = new PhantomReference<PhantomReferenceTest>(obj, phantomQueue);
try {
//不可获取虚引用中的对象:返回null
System.out.println(phantomRef.get());
obj = null;//将强引用去除
//第一次进行GC,由于obj这个强引用对象在finalize()方法中复活了,所以GC无法回收它
System.gc(); //第一次GC
Thread.sleep(1000); //由于GC线程优先级较低,这句话是为了让GC线程执行一下
if (obj == null) {
System.out.println("obj 是 null");
} else {
System.out.println("obj 可用");
}
System.out.println("第 2 次 gc");
obj = null;//将强引用去除
System.gc(); //第二次GC,一旦将obj对象回收,就会将此虚引用存放到引用队列中
Thread.sleep(1000);
if (obj == null) {
System.out.println("obj 是 null");
} else {
System.out.println("obj 可用");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}