java面试题整理(6)

本文围绕Java的垃圾回收机制展开,介绍了其优点、原理和回收机制,如分代复制、标记和增量回收。探讨了GC能否马上回收内存及主动通知方法,分析了Java中内存泄漏的场景。还提及深拷贝和浅拷贝,以及System.gc()、Runtime.gc()和finalize()方法的作用。

一. 垃圾回收的优点和原理。并考虑 2 种回收机制。
Java 语言中一个显著的特点就是引入了垃圾回收机制,使 C++程序员最头疼的内存管理的问题迎刃而解,它使得 Java 程序员在编写程序的时候不再需要考虑内存管理。由于有个垃圾回收机制,Java 中的对象不再有“作用域”的概念,只有对象的引用才有"作用域"。垃圾回收可以有效的防止内存泄露,有效的使用可以使用的内存。垃圾回收器通常是作为一个单独的低级别的线程运行,不可预知的情况下对内存堆中已经死亡的或者长时间没有使用的对象进行清楚和回收,程序员不能实时的调用垃圾回收器对某个对象或所有对象进行垃圾回收。
回收机制有分代复制垃圾回收和标记垃圾回收,增量垃圾回收。

二. 垃圾回收器的基本原理是什么?垃圾回收器可以马上回收内存吗?有什么办法主动通知虚拟机进行垃圾回收?
对于 GC 来说,当程序员创建对象时,GC 就开始监控这个对象的地址、大小以及使用情况。通常,GC 采用有向图的方式记录和管理堆(heap)中的所有对象。通过这种方式确定哪些对象是”可达的”,哪些对象是”不可达的”。当 GC 确定一些对象为“不可达”时,GC 就有责任回收这些内存空间。
可以。程序员可以手动执行 System.gc(),通知 GC 运行,但是 Java 语言规范并不保证 GC 一定会执行。

三. Java 中会存在内存泄漏吗,请简单描述。
所谓内存泄露就是指一个不再被程序使用的对象或变量一直被占据在内存中。Java 中有垃圾回收机制,它可以保证一对象不再被引用的时候,即对象变成了孤儿的时候,对象将自动被垃圾回收器从内存中清除掉。由于 Java 使用有向图的方式进行垃圾回收管理,可以消除引用循环的问题,例如有两个对象,相互引用,只要它们和根进程不可达的,那么 GC 也是可以回收它们的,例如下面的代码可以看到这种情况的内存回收:

import java.io.IOException;
public class GarbageTest {
/**
* @param args
* @throws IOException
*/
public static void main(String[] args) throws IOException
{
// TODO Auto-generated method stub
try {
gcTest();
} catch (IOException e) {
// TODO Auto-generated catch block
 e.printStackTrace();
}
System.out.println("has exited gcTest!");
System.in.read();
System.in.read();
System.out.println("out begin gc!");
for(int i=0;i<100;i++)
{
System.gc();
System.in.read();
System.in.read();
}
}
private static void gcTest() throws IOException {
System.in.read();
System.in.read();
Person p1 = new Person();
System.in.read();
System.in.read();
Person p2 = new Person();
p1.setMate(p2);
p2.setMate(p1);
System.out.println("before exit gctest!");
System.in.read();
 System.in.read();
System.gc();
System.out.println("exit gctest!");
}
private static class Person
{
byte[] data = new byte[20000000];
Person mate = null;
public void setMate(Person other)
{
mate = other;
}
}
}

Java 中的内存泄露的情况:长生命周期的对象持有短生命周期对象的引用就很可能发生内存泄露,尽管短生命周期对象已经不再需要,但是因为长生命周期对象持有它的引用而导致不能被回收,这就是 Java 中内存泄露的发生场景,通俗地说,就是程序员可能创建了一个对象,以后一直不再使用这个对象,这个对象却一直被引用,即这个对象无用但是却无法被垃圾回收器回收的,这就是 java中可能出现内存泄露的情况,例如,缓存系统,我们加载了一个对象放在缓存中 (例如放在一个全局 map 对象中),然后一直不再使用它,这个对象一直被缓存引用,但却不再被使用。检查 Java 中的内存泄露,一定要让程序将各种分支情况都完整执行到程序结束,然后看某个对象是否被使用过,如果没有,则才能判定这个对象属于内存泄露。如果一个外部类的实例对象的方法返回了一个内部类的实例对象,这个内部类对象被长期引用了,即使那个外部类实例对象不再被使用,但由于内部类持久外部类的实例对象,这个外部类对象将不会被垃圾回收,这也会造成内存泄露。
下面内容来自于网上(主要特点就是清空堆栈中的某个元素,并不是彻底把它从数组中拿掉,而是把存储的总数减少,在拿掉某个元素时,顺便也让它从数组中消失,将那个元素所在的位置的值设置为 null 即可):

public class Stack {
private Object[] elements=new Object[10];
private int size = 0;
public void push(Object e){
ensureCapacity();
elements[size++] = e;
 }
public Object pop(){
if( size == 0) throw new EmptyStackException();
return elements[--size];
}
private void ensureCapacity(){
if(elements.length == size){
Object[] oldElements = elements;
elements = new Object[2 * elements.length+1];
System.arraycopy(oldElements,0, elements, 0,size);
}
}
}

假如堆栈加了 10 个元素,然后全部弹出来,虽然堆栈是空的,没有我们要的东西,但是这是个对象是无法回收的,这个才符合了内存泄露的两个条件:无用,无法回收。但是就是存在这样的东西也不一定会导致什么样的后果,如果这个堆栈用的比较少,也就浪费了几个 K 内存而已,反正我们的内存都上 G 了,哪里会有什么影响,再说这个东西很快就会被回收的,有什么关系。下面看两个例子。

public class Bad{
 public static Stack s=Stack();
static{
s.push(new Object());
s.pop(); //这里有一个对象发生内存泄露
s.push(new Object()); //上面的对象可以被回收了,等于是自愈了
}
}

因为是 static,就一直存在到程序退出,但是我们也可以看到它有自愈功能,就是说如果你的 Stack 最多有 100 个对象,那么最多也就只有 100 个对象无法被回收其实这个应该很容易理解,Stack 内部持有 100 个引用,最坏的情况就是他们都是无用的,因为我们一旦放新的进去,以前的引用自然消失!内存泄露的另外一种情况:当一个对象被存储进 HashSet 集合中以后,就不能修改这个对象中的那些参与计算哈希值的字段了,否则,对象修改后的哈希值与最初存储进 HashSet 集合中时的哈希值就不同了,在这种情况下,即使在 contains 方法使用该对象的当前引用作为的参数去 HashSet 集合中检索对象,也将返回找不到对象的结果,这也会导致无法从 HashSet 集合中单独删除当前对象,造成内存泄露。

四. 深拷贝和浅拷贝。
简单来讲就是复制、克隆。
Person p=new Person(“张三”);
浅拷贝就是对对象中的数据成员进行简单赋值,如果存在动态成员或者指针就会报错。深拷贝就是对对象中存在的动态成员或指针重新开辟内存空间。

五. System.gc() 和 Runtime.gc() 会做什么事情?
这两个方法用来提示 JVM 要进行垃圾回收。但是,立即开始还是延迟进行垃圾回收是取决于 JVM 的。

六. finalize() 方法什么时候被调用?析构函数 (finalization) 的目的是什么?
垃圾回收器(garbage colector)决定回收某对象时,就会运行该对象的 finalize() 方法 但是在 Java 中很不幸,如果内存总是充足的,那么垃圾回收可能永远不会进行,也就是说 finalize() 可能永远不被执行,显然指望它做收尾工作是靠不住的。 那么finalize() 究竟是做什么的呢? 它最主要的用途是回收特殊渠道申请的内存。Java 程序有垃圾回收器,所以一般情况下内存问题不用程序员操心。但有一种 JNI(Java Native Interface)调用non-Java 程序(C 或 C++), finalize() 的工作就是回收这部分的内存。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值