面試官:過期引用你了解嗎?

學(xué)過JVM的小伙伴一定知道JVM中的四大引用,分別是強(qiáng)引用,軟引用,弱引用,虛引用。今天就來給大家說說什么是過期引用?
垃圾回收和內(nèi)存分配是JVM的兩大重點,今天通過一個過期引用的小例子串聯(lián)起這兩塊的知識。
先上代碼
public?class?Stack?{
????private?Object[] elements;
????private?int?size = 0;
????private?static?final?int?DEFAULT_INITIAL_CAPACITY = 16;
????public?Stack()?{
????????elements = new?Object[DEFAULT_INITIAL_CAPACITY];
????}
????public?void?push(Object e)?{
????????ensureCapcity();
????????elements[size++]=e;
????}
????public?Object pop(){
????????if(size==0){
????????????throw??new?EmptyStackException();
????????}
????????return?elements[--size];
????????//Object result=elements[--size];
????????//elements[size]=null;
????????//return result;
????}
????private?void?ensureCapcity(){
????????if(elements.length==size){
????????????elements= Arrays.copyOf(elements,2*size+1);
????????}
????}
}public?class?JprofilerUserTestMain?{
????public?static?void?main(String[] args)?throws InterruptedException {
????????//工具測試主類
????????Stack stack=new?Stack();
????????for?(int?i = 0; i < 1000; i++) {
????????????MyClass myClass=new?MyClass(i,""+i,(long)i);
????????????stack.push(myClass);
????????}
????????//等待15s
????????Thread.sleep(15000);
????????for?(int?i = 0; i < 500; i++) {
????????????stack.pop();
????????}
????????Thread.sleep(1000000);
????}
}首先創(chuàng)建了一個棧,棧的內(nèi)部使用使用數(shù)組來實現(xiàn),其中有兩個操作,入棧和出棧,順序入棧,倒置出棧。在Main函數(shù)中 for 循環(huán)先放入1000個元素,15s后 出棧500個元素。
對于我們來說 對于出棧的元素,我們往往不會再次從棧內(nèi)獲得。所以上面釋放的500個元素 應(yīng)該在之后被垃圾回收,但是通過Jprofiler工具,我發(fā)現(xiàn)在堆中 還是擁有1000個元素。原因是:stack棧中數(shù)組500到1000仍保存著釋放對象的引用,使得500個對象引用成為過期引用。無法被GC 判斷為無用對象進(jìn)行回收。
如果想要對這些對象進(jìn)行回收 只需要將pop方法中的數(shù)組引用指向為null,在一段時間后系統(tǒng)便會對500個對象進(jìn)行回收。
總結(jié)一下
過期引用指的是永遠(yuǎn)不會被解除的引用
在我們的stack例子中,凡是在elements數(shù)組的”活動范圍“之外的任何引用都是過期的,這里的活動部分指的是elements中下標(biāo)小于size的那些元素。
過期引用導(dǎo)致的問題
過期引用會導(dǎo)致內(nèi)存泄漏,內(nèi)存泄露,所謂泄露,就是原來被分配后的內(nèi)存,在失去利用價值后,應(yīng)該還給系統(tǒng)以重復(fù)利用,但卻沒還給系統(tǒng),導(dǎo)致系統(tǒng)可用內(nèi)存越來越少。
GC如何判斷一個對象是否可以回收,使用兩種方式,一種是引用計數(shù)算法和可達(dá)性分析算法,引用計數(shù)算法 有著引用之間相互嵌套風(fēng)險,所以可達(dá)性分析算法為主要使用,GC root鏈中對象對象可達(dá)則無法被回收,若多個對象和數(shù)組對象 有關(guān),那么垃圾回收機(jī)制不僅不會處理這個對象,而且也不會處理被這個對象所引用的其他對象。即使只有少量幾個對象引用被無意識的保留下來,也會有許許多多的對象被排除在垃圾回收機(jī)制之外。從而對性能造成潛在的巨大影響。
java源碼中為避免過期引用如何實現(xiàn)
public?synchronized?E pop()?{
????E obj;
????int?????len = size();
????obj = peek();
????removeElementAt(len - 1);
????return?obj;
}public?synchronized?void?removeElementAt(int?index)?{
????modCount++;
????if?(index >= elementCount) {
????????throw?new?ArrayIndexOutOfBoundsException(index + " >= "?+
?????????????????????????????????????????????????elementCount);
????}
????else?if?(index < 0) {
????????throw?new?ArrayIndexOutOfBoundsException(index);
????}
????int?j = elementCount - index - 1;
????if?(j > 0) {
????????System.arraycopy(elementData, index + 1, elementData, index, j);
????}
????elementCount--;
????elementData[elementCount] = null; /* to let gc do its work */
}stack類繼承vector類 ?進(jìn)入vector類中可以看到最后一行代碼,以及它的注釋。
作者:Deciscive
鏈接:juejin.im/post/6844904071657160711
