JAVA 面试知识点 2 --空间分配(Garbage Collection)

Garbage Collection在面试里还是经常被问到的,然而,java的Garbage Collection是自动的,所以并不会经常用到这些玩意儿。一切为了offer:
目录:

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
      一个示意图: stack and heap 堆和栈
      图片来源: 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
heap and stack图片来源: 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)
备注: daemon thread: A daemon thread is a thread that does not prevent the JVM from exiting when the program finishes but the thread is still running.
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值