Android实习面试准备——java基础(二)

1、Java中有几种引用关系,它们的区别是什么

        java中数据类型分为基本数据类型和引用类型,指向对象的变量即是引用变量,通过引用对堆中的对象进行操作。java的垃圾回收也是根据引用来决定。在JDK1.2以前,如果引用类型的数据的值是另一块内存的地址,就称这个引用数据为这块内存或者对象的引用,但是这太绝对了,一个对象只有被引用和没被引用两种状态,引用了就不被回收,没被引用就回收。如果我们想表示那些“内存不足时回收,内存充足时保留”的对象就没办法了。

        所以java对引用的概念进行了扩展,分为了四种引用:强引用、软引用、弱引用、虚引用,引用强度从左到右依次减弱。这样就可以让程序自己决定对象的生命周期,垃圾回收器即根据不同的引用进行不同的处理。

        (1)强引用:Object obj = new Object(),把一个对象赋给一个变量,这个引用变量就是一个强引用。当一个对象被一个强引用变量引用时,除非超过了引用的作用域,否则是不可能被垃圾回收器回收的。

        (2)软引用:SoftReference<Object> s = new SoftReference<Object>(obj),用来描述有用但非必需的对象,当内存不足的时候才会软引用引用的对象进行回收。一般用在对内存敏感的程序中,比如高速缓存。

        (3)弱引用:WeakReference<Object> w = new WeakReference<Object>(obj),用来描述非必需对象,不管内存是否足够,都会对其进行回收。ThreadLocal中就存在着弱引用,ThreadLocal之后在Handler机制中再总结。

        (4)虚引用:虚引用对对象的生命周期完全没有影响,如果一个对象被虚引用引用,那么就和没有引用一样,任何时候都可能被垃圾回收,不能单独使用,也不能通过虚引用操作对象(PhantomReference.get()总是返回null)。

        PhantomReference<Object> phantomReference = new PhantomReference<Object>(obj,referenceQueue),referenceQueue是引用队列,关联引用队列后,当垃圾回收释放对象内存的时候,会将引用加入到引用队列中,在回收对象的内存之前进行一些操作,相当于通知一样。所以虚引用的作用主要是跟踪对象的垃圾回收状态。

2、GC收集算法有哪些?它们的特点是什么

        (1)标记-清除算法:通过可达性分析之后存活的对象保留,需要回收的对象进行标记,然后将其起始地址和结束地址放到一个空闲地址链表中,下一次内存分配的时候从这个链表中进行查找。这个方法速度快,但是容易产生内存碎片。

        (2)标记-整理算法:通过可达性分析之后,将存活的对象都整理到内存的一端,并清理边界之外的空间。这个方法不会产生内存碎片,但是牵扯到对象的移动,地址的改变,速度慢。

        (3)复制算法:将现有的内存空间分为两块,称为from区和to区。首先进行可达性分析,将from中存活的对象复制到to区,然后交换from和to。这个方法不会产生内存碎片,但是会占用双倍的内存空间。若对象存活率高的话,要是采用这个算法,就会进行过多的复制操作,所以复制算法适用于对象存活率低的场景。

        (4)分代回收:新创建的对象在新生代中分配内存,此区域的对象一般生命周期较短,垃圾回收的效率也很高,一般采用的是复制算法。如果经过多次垃圾回收后依然存活,则晋升至老年代中。

        新生代分为伊甸园Eden区、from区和to区,新对象创建时默认采用伊甸园Eden中的空间,Eden空间不足的时候会触发minor gc,将垃圾对象回收清除,并将Eden和幸存区from中存活的对象复制到幸存区to中,然后对象年龄加1,并且交换from和to。 幸存区中的对象寿命如果超过阈值(默认15),就会晋升到老年代。如果新创建的对象比较大,并且新生代空间不足,也会直接放至老年代。

        而老年代的对象因为生命周期较长,不需要过多的复制操作,一般采用标记-整理算法。

3、如何判断一个对象是否被回收,有哪些GC算法,实际虚拟机使用最多的是什么GC算法

        判断一个对象是否能被回收有两个方法:

        (1)引用计数法:通过判断对象的引用数量来决定是否可以被回收。每个对象实例都有一个回收计数器,引用就+1,完成引用就-1。任何引用数量为0的对象都可以被回收。这个方法的优点是执行效率高,程序执行受影响小。但是没办法检测出循环引用的情况,这会导致内存泄漏。

        (2)可达性分析算法:通过判断对象的引用链是否可达来决定是否可以被回收。以GC root对象为起始点往下搜索,搜索走过的路径就称为引用链。

        GC root对象有:

        a. 虚拟机栈中局部变量表引用的对象(正在执行的方法中局部变量指向的对象)

        b. 方法区中静态引用指向的对象(类中static修饰的变量指向的对象)

        c. 方法区中常量引用的对象

        d. 仍处于存活状态中的线程对象

        e. Native方法中JNI引用的对象

        实际上虚拟机使用的最多的是可达性分析算法。

4、Jvm内存结构说一下

        分析JVM的内存结构其实就是分析JVM的运行时数据存储区域。JVM的运行时数据区分为方法区、堆、虚拟机栈、本地方法栈、程序计数器。其中,方法区和堆是线程共享的,虚拟机栈、本地方法栈、程序计数器为线程私有的。

        方法区中存储的是类的相关信息、常量池、静态常量及变量。JDK1.8以后用元空间MetaSpace替代方法区,元空间不在JVM里,而是在本地内存中。常量池存储的是编译器生成的各种字面量及符号引用,字面量包括final修饰的常量、基本数据类型的值、字符串常量,符号引用包括类和接口的全限定名、方法名和描述符、字段名和描述符。

        堆主要用来存放new出来的对象,所有对象实例及数组都是在堆上分配,垃圾回收就是收集堆上对象所占用的内存空间。堆分为新生代和老年代,新生代又可分为伊甸园Eden和幸存区Survivor,Survivor区又分为from区和to区。

        虚拟机栈和本地方法栈都是JVM中的栈,只不过虚拟机栈是为java方法服务的,而本地方法栈是为JVM用到的native方法服务的,native方法使用C或C++语言实现的,主要用来和操作系统交互,更接近于底层。栈是线程私有的,生命周期和线程相同,每个线程都会分配一个栈空间。栈由栈帧组成,每调用一个方法时都会创建一个栈帧,将其入栈,当方法调用完毕后,又会出栈。栈帧中存储了局部变量表、操作数栈、动态链接和方法出口等信息。

        程序计数器是线程私有的,其实就是一小块内存空间,记录的是正在执行的虚拟机字节码指令的地址。一个线程的执行,是通过字节码解释器修改程序计数器的值,来获取下一条需要执行的字节码的指令,从而确保线程的正确执行。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值