Garbage Collection在面试里还是经常被问到的,然而,java的Garbage Collection是自动的,所以并不会经常用到这些玩意儿。一切为了offer:
目录:
- 1. Stack(栈) 和 Heap(堆)
- 2. 垃圾回收机制(Garbage Collection)
- 3.如何判断一个object是不是可以被回收
- 4. 一些关于Garbage Collection的面试题
1. Stack(栈) 和 Heap(堆)
为了好好学习Garbage Collection 先看看什么是memory的 Stack 和 Heap好了。
- **Stack(栈)**是给一个线程执行的时候用的空间。当一个方法被调用的时候,堆会给它一块儿空间让它存放变量。 当方法返回后,这个空间就可以被释放然后被其他方法用了。
- **Heap(堆)**是给动态分配(dynamic allocation)用的空间。没有固定的分配和释放规则。可以随意的分配和释放。
- 一个application只有一个heap,但是application里的每个线程都可以拿到一个stack。 所以一个stack是跟着一个thread生而生,死而死。一个heap跟着一个application生而生,死而死。
- 当一个object被创建的时候,这个object被放在heap里,但是它的reference被放在stack里。 stack memory只存储一些local primitive 变量和reference。
- heap里的Object是globally的,但是stack里的变量不能被其他线程访问。
- stack的大小在thread创建的时候设置的(-Xss。heap的大小是application启动的时候设置的(-Xms 和 -Xmx),但是可以随着运行要求os分配更多空间而增长。
- stack更快更小。
- 因为stack的访问模式(LIFO)使得分配和释放空间很简单。
- stack中的每个字节都经常被频繁地访问,这意味着它往往被映射到处理器的缓存。
- heap的另一个性能损失是heap(主要是全局资源)通常必须是多线程安全的,即每个分配和释放需要与程序中的“所有”其他stack访问同步。
- 当 stack 的memory满了 Java runtime 抛出
java.lang.StackOverFlowError
, 当 heap 的memory 满了的时候, java 抛出java.lang.OutOfMemoryError: Java Heap Space error
。
一个示意图:
图片来源: vikashazrati.wordpress.com
举一个栗子:
public class Memory {
public static void main(String[] args) { // Line 1
int i=1; // Line 2
Object obj = new Object(); // Line 3
Memory mem = new Memory(); // Line 4
mem.foo(obj); // Line 5
} // Line 9
private void foo(Object param) { // Line 6
String str = param.toString(); Line 7
System.out.println(str);
} // Line 8
}
代码来源: https://www.journaldev.com/4098/java-heap-space-vs-stack-memory
图片来源: https://www.journaldev.com/4098/java-heap-space-vs-stack-memory
具体流程解释在: https://www.journaldev.com/4098/java-heap-space-vs-stack-memory
每次用new
这个关键词创建object,都会在heap上开辟一块儿空间
2. 垃圾回收机制(Garbage Collection)
小呆毛hin生气: Garbage Collection 什么的不是JVM 自己的工作么,面试为神马还要问!**
2. 1.基本知识
- 垃圾回收(Garbage Collection 之后简称GC)是发生在heap里面的,因为stack里的空间在方法return或者结束之后就回收了。
- GC 是java用来回收那些不再被reference到的object 所占用的空间的。reference 在stack里面,方法结束之后空间释放,reference就没了,但是heap里的object还在。所以需要GC。
- GC 是一个**守护线程 (Daemon Thread),优先级最低。**如果要手动强制回收一遍,可以调用
System.gc()
或者Runtime.getRuntime().gc()
- 在知识点1里面讲到object的几个方法,里面有个
finalize()
. 就是当这个object被回收的时候,这个方法就会被调用。一旦调用这个object就没了,所以这个方法只能被用一次。 - override
finalize()
要慎重,不要造成不可回收的垃圾(memory leak)。
2. 2.heap里的Three Generations objects
为了方便garbage collection, Java Heap 被分成了三代:新生代(young generation), 老年代(old generation), 和持久代(permanent generation)。
新生代 Young Generation
当一个object被创建的时候,就被放在新生代(young generation)里面。 当新生代空间被填满了, 就会进行一次垃圾回收(Garbage Collection)。这种Garbage Collection被称为Minor GC. Young Generation 又被分为三部分: Eden Memory(伊甸园) 和两个Survivor Memory(存活区?大难不死区?) .
- 大多数的新创建的object都被放在Eden memory。
- 当 Eden Memory 的空间满了,就会执行一次 Minor GC。 有一些object被回收了,还有一些object被留下了,被留下的object就被移到了survivor Memory区.
- 两个survivor memory 区永远有一个是空的,Minor GC也会扫描检查一下那个有object的 survivor memory区的objects 然后把一些回收,剩下的移到另一个survivor memory区。
- 如果有一些object在经过很多轮GC之后依然存活,它们就会被移到老年区(Old generation memory space)。 通常会设置一个年轻区的年龄阈值, 超过这个阈值,就被移到老年区。
老年代 Old Generation
老年区里面的object都是经历过很多轮的Minor GC之后依然存活下来的。老年代空间被填满之后也会执行一次GC。 这种Garbage Collection被成为Major GC。通常需要话费更长时间。
- 所以Young Generation里面放的都是一些活的比较短的object。 所以Minor GC 很快, application也不会受到影响。
- Major GC要检查所有的object,所以花费的时间要更久一些,所以就会影响application的执行,造成timeout。 要尽量减少Major GC的次数。
持久代(permanent generation or Perm Gen)
持久区里面放的都是一些不会被垃圾回收的东西。比方说classes和methods的描述和他们本身。这些东西只会在程序运行完之后被回收。
permanent generation不是heap memory的一部分 大概就是heap是用来放object的,那用来创建object的class的描述,还有一些其他的程序的metadata(描述data的data)放在permanent generation就不要在heap里了。如果一定要纠结:
https://blogs.oracle.com/jonthecollector/presenting-the-permanent-generation
- Method Area 是持久代的一部分,用来存放class structure (runtime constants and static variables)的。还有方法和构造函数的代码本身.
- Runtime Constant Pool 是一个以class为单位的runtime constant pool. 它用来存放一个class 的runtime constants and static methods. 是 method area的一部分
- Memory Pool是JVM memory managers 创建的用来管理一些immutable objects. String Pool 是一个很好的例子。Memory Pool 可以在heap 上也可以在 permanent generation上,取决于JVM memory manager 想怎样。
3.如何判断一个object是不是可以被回收
3.1. 可以被回收(eligible for garbage collection in Java)的条件
- 所有指向这个object的reference都指向null了。
- 一个object在一个block里面被创建,代码已经执行完这个block了。(out scope)
- 一个object的爸爸被设置成null了。如果一个object里面包含了一个指向其他object的reference,如果这个object的reference被设置成null了,那么它包含的子孙都可以被回收。
3.2. java里的四种reference
1. Strong References
默认的reference,大部分的平时见到的都是这个。只有在强制赋值成null了之后,原来指向的object才会有可能被回收。
MyClass obj = new MyClass (); //用new在heap上开辟出一块儿空间,建立一个object
obj = null; // obj这个玩意儿没办法指向在heap上的那个object了,所以heap上的那个object可以被回收了。
2. Weak References
如果要用这种reference,必须在声明的时候特别说明。 一个比较好的例子是 WeakHashMap
- 如果JVM 发现一个object只有weak references, 这个object就会被标记成可以被回收,在下一轮回收的时候会被回收
- 必须用
java.lang.ref.WeakReference
来创建这类reference - 这类reference在现实世界中通常用来建立对数据库的链接。当数据库被关上时,就回收掉。
来个例子:
import java.lang.ref.WeakReference;
class WeakTest
{
public void printWeak()
{
System.out.println("weak");
}
}
public class Test
{
public static void main(String[] args)
{
WeakTest wt = new WeakTest(); // Strong Reference
wt.printWeak();
WeakReference<Gfg> weakref = new WeakReference<Gfg>(wt); // 创建一个Weak Reference指向 一个像是WeakTest的object指向wt
wt = null; //现在wt可以被回收了.
//但是这个object还是可以通过weak reference找回来。
wt = weakref.get();
wt.printWeak();
}
}
3. Soft References
这类reference指向的object只在memory快用完的时候才会被回收。
- 必须用
java.lang.ref.SoftReference 来创
建这类reference.
代码例子:
import java.lang.ref.SoftReference;
class SoftTest
{
public void printSoft()
{
System.out.println("Soft");
}
}
public class Test
{
public static void main(String[] args)
{
SoftTest st = new SoftTest(); // Strong Reference
st.printSoft();
SoftReference<Gfg> softref = new SoftReference<Gfg>(st); //创建一个soft reference也指向st
st = null; //st可以回收了.但是只会在memory快用完的时候回收。
//回收之前还是可以找回来的
st = softref.get();
st.printSoft();
}
}
4.Phantom References
这一类reference指向的object可以被回收了,但是在用finalize()
释放它们之前会把他们放到一个 reference queue
里。用到 java.lang.ref.PhantomReference
来创建他们。(所以,它们为什么要存在?)
例子:
import java.lang.ref.*;
class PhantomTest
{
public void printPhantom()
{
System.out.println("Phantom");
}
}
public class Test
{
public static void main(String[] args)
{
PhantomTest pt = new PhantomTest(); //Strong Reference
pt.printPhantom();
//Creating reference queue
ReferenceQueue<Gfg> refQueue = new ReferenceQueue<Gfg>();
PhantomReference<Gfg> phantomRef = null;
phantomRef = new PhantomReference<Gfg>(pt,refQueue);
pt = null;
pt = phantomRef.get(); //It always returns null. (所以这玩意儿的意义是???黑人问号脸)
pt.x(); //It shows NullPointerException.
}
}
4. 一些关于Garbage Collection的面试题
1. 如何定义一个无法被回收的object?
答案:把这个object定义成一个class里的static final 变量.
2.如何尽量减少回收次数?
答案:用object pool,比方说factory pattern.
3. helloworld program里面有几个线程?
答案:2个(Garbage Collection是一个daemon thread)