大战熟女丰满人妻av-荡女精品导航-岛国aaaa级午夜福利片-岛国av动作片在线观看-岛国av无码免费无禁网站-岛国大片激情做爰视频

第一部分 Java基礎(chǔ)
第二部分 Java進(jìn)階

Java GC面試題及答案(1~5題)

1、既然有GC機(jī)制,為什么還會(huì)有內(nèi)存泄露的情況?

理論上Java因?yàn)橛欣厥諜C(jī)制(GC)不會(huì)存在內(nèi)存泄露問題(這也是Java被廣泛使用于服務(wù)器端編程的一個(gè)重要原因)。然而在實(shí)際開發(fā)中,可能會(huì)存在無用但可達(dá)的對(duì)象,這些對(duì)象不能被GC回收,因此也會(huì)導(dǎo)致內(nèi)存泄露的發(fā)生。

● 例如hibernate的Session(一級(jí)緩存)中的對(duì)象屬于持久態(tài),垃圾回收器是不會(huì)回收這些對(duì)象的,然而這些對(duì)象中可能存在無用的垃圾對(duì)象,如果不及時(shí)關(guān)閉(close)或清空(flush)一級(jí)緩存就可能導(dǎo)致內(nèi)存泄露。

下面例子中的代碼也會(huì)導(dǎo)致內(nèi)存泄露。

import java.util.Arrays;
import java.util.EmptyStackException;
public class MyStack<T> {
    private T[] elements;
    private int size = 0;
    private static final int INIT_CAPACITY = 16;
    public MyStack() {
        elements = (T[]) new Object[INIT_CAPACITY];
    }
    public void push(T elem) {
        ensureCapacity();
        elements[size++] = elem;
    }
    public T pop() {
        if (size == 0) throw new EmptyStackException();
        return elements[--size];
    }

    private void ensureCapacity() {
        if (elements.length == size) {
            elements = Arrays.copyOf(elements,2 * size + 1);
        }
    }
}

上面的代碼實(shí)現(xiàn)了一個(gè)棧(先進(jìn)后出(FILO))結(jié)構(gòu),乍看之下似乎沒有什么明顯的問題,它甚至可以通過你編寫的各種單元測(cè)試。然而其中的pop方法卻存在內(nèi)存泄露的問題,當(dāng)我們用pop方法彈出棧中的對(duì)象時(shí),該對(duì)象不會(huì)被當(dāng)作垃圾回收,即使使用棧的程序不再引用這些對(duì)象,因?yàn)闂?nèi)部維護(hù)著對(duì)這些對(duì)象的過期引用(obsolete reference)。在支持垃圾回收的語言中,內(nèi)存泄露是很隱蔽的,這種內(nèi)存泄露其實(shí)就是無意識(shí)的對(duì)象保持。如果一個(gè)對(duì)象引用被無意識(shí)的保留起來了,那么垃圾回收器不會(huì)處理這個(gè)對(duì)象,也不會(huì)處理該對(duì)象引用的其他對(duì)象,即使這樣的對(duì)象只有少數(shù)幾個(gè),也可能會(huì)導(dǎo)致很多的對(duì)象被排除在垃圾回收之外,從而對(duì)性能造成重大影響,極端情況下會(huì)引發(fā)Disk Paging(物理內(nèi)存與硬盤的虛擬內(nèi)存交換數(shù)據(jù)),甚至造成OutOfMemoryError。

2、Java中為什么會(huì)有GC機(jī)制呢?

安全性考慮;--for security.

減少內(nèi)存泄露;--erase memory leak in some degree.

減少程序員工作量。--Programmers don't worry about memory releasing.

3、對(duì)于Java的GC哪些內(nèi)存需要回收?

內(nèi)存運(yùn)行時(shí)JVM會(huì)有一個(gè)運(yùn)行時(shí)數(shù)據(jù)區(qū)來管理內(nèi)存。

● 它主要包括5大部分:

1.程序計(jì)數(shù)器(Program CounterRegister);

2.虛擬機(jī)棧(VM Stack);

3.本地方法棧(Native Method Stack);

4.方法區(qū)(Method Area);

5.堆(Heap)。

而其中程序計(jì)數(shù)器、虛擬機(jī)棧、本地方法棧是每個(gè)線程私有的內(nèi)存空間,隨線程而生,隨線程而亡。例如棧中每一個(gè)棧幀中分配多少內(nèi)存基本上在類結(jié)構(gòu)確定是哪個(gè)時(shí)就已知了,因此這3個(gè)區(qū)域的內(nèi)存分配和回收都是確定的,無需考慮內(nèi)存回收的問題。

但方法區(qū)和堆就不同了,一個(gè)接口的多個(gè)實(shí)現(xiàn)類需要的內(nèi)存可能不一樣,我們只有在程序運(yùn)行期間才會(huì)知道會(huì)創(chuàng)建哪些對(duì)象,這部分內(nèi)存的分配和回收都是動(dòng)態(tài)的,GC主要關(guān)注的是這部分內(nèi)存。總而言之,GC主要進(jìn)行回收的內(nèi)存是JVM中的方法區(qū)和堆。

4、Java的GC什么時(shí)候回收垃圾?

在面試中經(jīng)常會(huì)碰到這樣一個(gè)問題(事實(shí)上筆者也碰到過):如何判斷一個(gè)對(duì)象已經(jīng)死去?

很容易想到的一個(gè)答案是:對(duì)一個(gè)對(duì)象添加引用計(jì)數(shù)器。每當(dāng)有地方引用它時(shí),計(jì)數(shù)器值加1;當(dāng)引用失效時(shí),計(jì)數(shù)器值減1.而當(dāng)計(jì)數(shù)器的值為0時(shí)這個(gè)對(duì)象就不會(huì)再被使用,判斷為已死。是不是簡(jiǎn)單又直觀。然而,很遺憾。這種做法是錯(cuò)誤的!為什么是錯(cuò)的呢?事實(shí)上,用引用計(jì)數(shù)法確實(shí)在大部分情況下是一個(gè)不錯(cuò)的解決方案,而在實(shí)際的應(yīng)用中也有不少案例,但它卻無法解決對(duì)象之間的循環(huán)引用問題。比如對(duì)象A中有一個(gè)字段指向了對(duì)象B,而對(duì)象B中也有一個(gè)字段指向了對(duì)象A,而事實(shí)上他們倆都不再使用,但計(jì)數(shù)器的值永遠(yuǎn)都不可能為0,也就不會(huì)被回收,然后就發(fā)生了內(nèi)存泄露。

● 正確的做法應(yīng)該是怎樣呢?

在Java,C#等語言中,比較主流的判定一個(gè)對(duì)象已死的方法是:可達(dá)性分析(Reachability Analysis).所有生成的對(duì)象都是一個(gè)稱為"GC Roots"的根的子樹。從GC Roots開始向下搜索,搜索所經(jīng)過的路徑稱為引用鏈(Reference Chain),當(dāng)一個(gè)對(duì)象到GC Roots沒有任何引用鏈可以到達(dá)時(shí),就稱這個(gè)對(duì)象是不可達(dá)的(不可引用的),也就是可以被GC回收了。

● 無論是引用計(jì)數(shù)器還是可達(dá)性分析,判定對(duì)象是否存活都與引用有關(guān)!那么,如何定義對(duì)象的引用呢?

我們希望給出這樣一類描述:當(dāng)內(nèi)存空間還夠時(shí),能夠保存在內(nèi)存中;如果進(jìn)行了垃圾回收之后內(nèi)存空間仍舊非常緊張,則可以拋棄這些對(duì)象。所以根據(jù)不同的需求,給出如下四種引用,根據(jù)引用類型的不同,GC回收時(shí)也會(huì)有不同的操作:

● 強(qiáng)引用(Strong Reference):Object obj=new Object();只要強(qiáng)引用還存在,GC永遠(yuǎn)不會(huì)回收掉被引用的對(duì)象。

● 軟引用(Soft Reference):描述一些還有用但非必需的對(duì)象。在系統(tǒng)將會(huì)發(fā)生內(nèi)存溢出之前,會(huì)把這些對(duì)象列入回收范圍進(jìn)行二次回收(即系統(tǒng)將會(huì)發(fā)生內(nèi)存溢出了,才會(huì)對(duì)他們進(jìn)行回收)

● 弱引用(Weak Reference):程度比軟引用還要弱一些。這些對(duì)象只能生存到下次GC之前。當(dāng)GC工作時(shí),無論內(nèi)存是否足夠都會(huì)將其回收(即只要進(jìn)行GC,就會(huì)對(duì)他們進(jìn)行回收。)

● 虛引用(Phantom Reference):一個(gè)對(duì)象是否存在虛引用,完全不會(huì)對(duì)其生存時(shí)間構(gòu)成影響。關(guān)于方法區(qū)中需要回收的是一些廢棄的常量和無用的類。

1.廢棄的常量的回收。這里看引用計(jì)數(shù)就可以了。沒有對(duì)象引用該常量就可以放心的回收了。

2.無用的類的回收。什么是無用的類呢?

A.該類所有的實(shí)例都已經(jīng)被回收。也就是Java堆中不存在該類的任何實(shí)例;

B加載該類的ClassLoader已經(jīng)被回收;

C.該類對(duì)應(yīng)的java.lang.Class對(duì)象沒有任何地方被引用,無法在任何地方通過反射訪問該類的方法。總而言之:對(duì)于堆中的對(duì)象,主要用可達(dá)性分析判斷一個(gè)對(duì)象是否還存在引用,如果該對(duì)象沒有任何引用就應(yīng)該被回收。而根據(jù)我們實(shí)際對(duì)引用的不同需求,又分成了4種引用,每種引用的回收機(jī)制也是不同的。對(duì)于方法區(qū)中的常量和類,當(dāng)一個(gè)常量沒有任何對(duì)象引用它,它就可以被回收了。而對(duì)于類,如果可以判定它為無用類,就可以被回收了。

5、通過10個(gè)示例來初步認(rèn)識(shí)Java8中的lambda表達(dá)式

● 用lambda表達(dá)式實(shí)現(xiàn)Runnable

// Java 8 之前:

new Thread(new Runnable(){
    @Override
    public void run(){
        System.out.println("Before Java8, too much code for too little to do");
    }}).start();
    //Java 8 方式:
    new Thread(()->System.out.println("In Java8, Lambda expression rocks !!")).start();

輸出:

too much code,for too little to do

Lambda expression rocks!!

這個(gè)例子向我們展示了Java 8 lambda表達(dá)式的語法。你可以使用lambda寫出如下代碼:

(params) -> expression (params) -> statement
(params) -> { statements }

例如,如果你的方法不對(duì)參數(shù)進(jìn)行修改、重寫,只是在控制臺(tái)打印點(diǎn)東西的話,那么可以這樣寫:

() -> System.out.println("Hello Lambda Expressions");

如果你的方法接收兩個(gè)參數(shù),那么可以寫成如下這樣:

(int even, int odd) -> even + odd

順便提一句,通常都會(huì)把lambda表達(dá)式內(nèi)部變量的名字起得短一些。這樣能使代碼更簡(jiǎn)短,放在同一行。所以,在上述代碼中,變量名選用a、b或者x、y會(huì)比even、odd要好。

● 使用Java 8 lambda表達(dá)式進(jìn)行事件處理

如果你用過Swing API編程,你就會(huì)記得怎樣寫事件監(jiān)聽代碼。這又是一個(gè)舊版本簡(jiǎn)單匿名類的經(jīng)典用例,但現(xiàn)在可以不這樣了。你可以用lambda表達(dá)式寫出更好的事件監(jiān)聽代碼,如下所示:

// Java 8 之前:
JButton show = new JButton("Show"); show.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        System.out.println("Event handling without lambda expression is boring");
    }
});
// Java 8 方式:
show.addActionListener((e) -> {
    System.out.println("Light, Camera, Action !! Lambda expressions Rocks");
});

● 使用Java 8 lambda表達(dá)式進(jìn)行事件處理 使用lambda表達(dá)式對(duì)列表進(jìn)行迭代

如果你使過幾年Java,你就知道針對(duì)集合類,最常見的操作就是進(jìn)行迭代,并將業(yè)務(wù)邏輯應(yīng)用于各個(gè)元素,例如處理訂單、交易和事件的列表。由于Java是命令式語言,Java 8之前的所有循環(huán)代碼都是順序的,即可以對(duì)其元素進(jìn)行并行化處理。如果你想做并行過濾,就需要自己寫代碼,這并不是那么容易。通過引入lambda表達(dá)式和默認(rèn)方法,將做什么和怎么做的問題分開了,這意味著Java集合現(xiàn)在知道怎樣做迭代,并可以在API層面對(duì)集合元素進(jìn)行并行處理。下面的例子里,我將介紹如何在使用lambda或不使用lambda表達(dá)式的情況下迭代列表。你可以看到列表現(xiàn)在有了一個(gè)forEach()方法,它可以迭代所有對(duì)象,并將你的lambda代碼應(yīng)用在其中。

// Java 8 之前:
List features = Arrays.asList("Lambdas", "Default Method", "Stream API","Date and Time API");
for (String feature : features) {
    System.out.println(feature);
}
// Java 8 之后:
List features = Arrays.asList("Lambdas", "Default Method", "Stream API","Date and Time API");
features.forEach(n -> System.out.println(n));
// 使用 Java 8 的方法引用更方便,方法引用由::雙冒號(hào)操作符標(biāo)示,
// 看起來像 C++的作用域解析運(yùn)算符
features.forEach(System.out::println);

輸出:

Lambdas Default Method Stream API

Date and Time API

列表循環(huán)的最后一個(gè)例子展示了如何在Java 8中使用方法引用(method reference)。你可以看到C++里面的雙冒號(hào)、范圍解析操作符現(xiàn)在在Java 8中用來表示方法引用。

● 使用lambda表達(dá)式和函數(shù)式接口Predicate

除了在語言層面支持函數(shù)式編程風(fēng)格,Java 8也添加了一個(gè)包,叫做java.util.function。它包含了很多類,用來支持Java的函數(shù)式編程。其中一個(gè)便是Predicate,使用java.util.function.Predicate函數(shù)式接口以及l(fā)ambda表達(dá)式,可以向API方法添加邏輯,用更少的代碼支持更多的動(dòng)態(tài)行為。下面是Java 8 Predicate的例子,展示了過濾集合數(shù)據(jù)的多種常用方法。Predicate接口非常適用于做過濾。

public static void main(String[]args){
    List languages=Arrays.asList("Java", "Scala","C++", "Haskell", "Lisp");
    System.out.println("Languages which starts with J :");
    filter(languages, (str)->str.startsWith("J"));
    System.out.println("Languages which ends with a ");
    filter(languages, (str)->str.endsWith("a"));
    System.out.println("Print all languages :");
    filter(languages, (str)->true);
    System.out.println("Print no language : ");
    filter(languages, (str)->false);
    System.out.println("Print language whose length greater than 4:");
    filter(languages, (str)->str.length()>4);
}
public static void filter(List names, Predicate condition){
    for(String name:names){
        if(condition.test(name)){
            System.out.println(name+" ");
        }
    }
}
// filter 更好的辦法--filter 方法改進(jìn)
public static void filter(List names, Predicate condition) {
    names.stream().filter((name)->(condition.test(name))).forEach((name)->
	{System.out.println(name + " ");
    });
}

可以看到,Stream API的過濾方法也接受一個(gè)Predicate,這意味著可以將我們定制的filter()方法替換成寫在里面的內(nèi)聯(lián)代碼,這就是lambda表達(dá)式的魔力。另外,Predicate接口也允許進(jìn)行多重條件的測(cè)試。

全部教程
主站蜘蛛池模板: 噜噜色图 | 91福利刘玥国产在线观看 | 国内精品久久久久久影院老狼 | 久热精品男人的天堂在线视频 | h视频在线观看免费网站 | 日本精品久久久久中文字幕8 | 天天艹夜夜艹 | 91中文字幕在线一区 | 国产精品 第二页 | 成 人 黄 色 大 片 | 91成年人视频 | 亚洲免费视频网 | 韩国色三级伦不卡高清在线观看 | 中文字幕精品视频在线 | 免费看日韩欧美一级毛片 | 亚洲欧洲日韩在线 | 亚洲天天做日日做天天看2018 | 人人爱天天做夜夜爽2020麻豆 | 天天天操天天天干 | 欧美成人在线免费视频 | 寡妇一级a毛片免费播放 | 亚洲激情视频 | 视频在线二区 | 久草在线视频在线 | 九九福利 | 视频播放在线观看精品视频 | 性欧美4k高清精品 | 中文国产成人精品少久久 | 最新亚洲情黄在线网站 | 伊人激情久久综合中文字幕 | 亚洲精品影院一区二区 | se成人国产精品 | 免费爱爱片 | 欧美日韩理论 | 久久国产精品亚洲77777 | 天海翼一区二区三区免费 | 99热久久国产精品这里有6 | 九九视频精品全部免费播放 | 91一区二区三区四区五区 | 五月天在线免费视频 | 久久99国产精品视频 |