33-面经整理

百度实习-2

介绍一下JVM运行时数据区?

程序计数器:线程私有,记录当前线程所执行的位置,这样当线程重新获得cpu的执行权的时候,就直接从记录的位置开始执行

本地方法栈:线程私有,为虚拟机使用到的Native方法服务,Native方法是java调用非java代码的接口

方法区:线程共享区域,用于存储已经被虚拟机加载的类信息,常量,静态变量,当方法区无法满足分配内存需求的时候,会抛出outofmemoryError的异常

虚拟机栈:线程私有,在每个方法执行的时候都会同时创建出一个栈帧,用于存储局部变量表,操作栈,动态链接,方法出口这些信息;每一个方法被调用直到执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程,这里存放基本数据类型,对象的引用等信息,这个区域可能有两种异常:当线程请求栈的深度大于虚拟机所允许的深度,将抛出stackoverflow异常,如果是虚拟机栈在动态扩展的时候无法申请到足够内存的时候会抛出outofmemoryError的异常

堆:线程共享区域,栈管理运行,堆管理存储,存储的是new出来的对象,不存放基本类型和对象引用,由于创建了大量的对象,垃圾回收器主要工作在这块区域,会发生outofMemoryError

介绍一下JVM的垃圾回收?

如何确定对象已经死亡?

  • 引用计数法:为对象的引用添加一个引用计数器,每当对象在一个地方被引用就+1,失去引用-1,如果为0就表示没有被引用,应该被回收
  • 可达性分析算法:从GCRoots的开始,沿着引用链进行搜索,凡是在引用链上的对象都不会被回收

GCRoots:虚拟机栈中被引用的对象,方法区中静态属性引用的对象,常量引用的对象以及本地方法栈中(JNI)引用的对象

 垃圾回收算法?

标记清除:标记存活对象,然后去堆中遍历所有没有被找到的对象进行垃圾回收,存在内存碎片问题

复制算法:内存分为两块,一块用完之后,把存活对象丢到另外一块,然后清除内存,空间利用率低下,该算法经常用于新生代,由于大部分新生代的对象都不会存活过第一次GC所以没必要1:1划分空间,可以使用一块大的Eden和两块小的Survivor空间,每次使用Eden和其中的一块Survivor,其大小一般是8:1:1,如果存活对象大于10%会直接丢到老年代

标记整理算法:针对老年代的特点,在标记清除的基础上会把存活的对象移动到内存的一端

分代回收:根据存活对象划分几块内存区域,根据各个年代的特定制定相应的算法

垃圾收集器?

新生代的所有收集器都基于复制算法,老年代基本都基于标记整理,特殊的是CMS基于标记清除,G1基于复制+标记整理

Serial收集器:单线程收集器,收垃圾的时候必须暂停所有线程

ParNew收集器:Serial的多线程版本,单核情况下没有Serial快

Parallel Scavenge收集器:多条垃圾收集线程并行的工作,此时用户线程依然处于等待状态

Serial Old:Serial的老年代版本

Parallel Old:Parallel Scavenge的老年代版本

CMS:可以做到回收线程与用户线程并行的垃圾收集器,其步骤是:初始标记,并发标记,重新标记,并发清除;缺点是无法收集浮动垃圾和空间碎片问题;

G1:优点是并行并发,分带收集,空间整合和可预测停顿,步骤为:初始标记,并发标记,最终标记,筛选回收;只有并发标记过程不会暂停用户线程

内存分配与回收策略

新生代GC(Minor GC):频繁,速度快,老年代GC(full GC):速度比Minor GC慢10倍以上

  • 对象分配在新生代的伊甸园区,当第一次触发Minor GC的时候,Eden区存活的对象会被丢到survivor的某一块区域,以后再触发的时候,Eden区的对象和Survivor区的对象一起,被转移到另一块Survivor区域;如果对象太大,Survivor的to区没有足够的空间,就会直接把对象丢到老年代,年龄大的对象也会被丢到老年代
  • 当老年代被占满的时候就会触发full GC,期间会停止所有线程等待GC完成,所以对待响应要求高的应用尽量区减少full GC从而避免响应超时问题

双亲委派模型

对于Java来讲只有两种类加载器:启动类加载器(c++实现)和其他所有类加载器(继承于ClassLoader),如果一个类加载器收到加载的请求,它首先不会自己加载,而是把这个请求委派给父类加载器,只有父类无法完成对子类的加载的时候,子类才会尝试区进行加载

  • BootStrap:加载rt.jar
  • Extension:加载扩展的jar包
  • App:加载指定class Path下的jar包

垃圾回收只回收堆中的对象?为什么要分新生代和老年代?为什么用复制算法和标记整理?

方法区也有需要回收的内容:废弃的常量或者是无用的类也是

根据对象存活时间来看,有的对象寿命长,有的短,因此应该将他们放在不同的区域进行同一管理

使用复制算法的原因是:新生代绝大部分都是垃圾对象,可以使用复制算法将小部分的存活对象复制到另一个区域,因为存活的对象少,所以复制的次数少;老年代都是些有用的对象,比方说你有1000张图片里面只有3张是没有用的,总不可能用复制算法把有用的全部复制一遍,还不如区找到这三张照片直接删除

如何保证不同线程之间的隔离,同一线程之间的共享,threadLocal的原理以及局限

其底层是ThreadLocalMap<ThreadLocalObject>的形式,本身是一个key-value键值对的格式,key是弱引用,threadlocal本身,value是存储的数据,ThreadLocal本身不存储该数据,它只是用来帮助存和取的一个工具

其实每个Thread都维护了一个ThreadLocalMap的变量,线程创建ThreadLocal的时候,其实是在自己的threadLocals变量里存放了值,别人没办法拿到,于是实现了线程的隔离

public class Thread implements Runnable {
    ThreadLocalMap threadLocals = null;
    ThreadLocalMap inheritableThreadLocals = null;
}

ThreadLocalMap底层其实还是一个Enrty的数组,为的是存放多个Enrty ,它处理哈希冲突的方式是,如果两个key是一样的,就覆盖,不一样就寻找数组的下一个位置,直到找到空位置为止

ThreadLocal的值被线程实例持有,它们都是位于堆上,只是通过一些技巧将可见性修改成了线程可见而不是放在栈空间,如果想要多线程都能访问到,可以使用如下代码也就是inheritableThreadLocal

private void test() {    
final ThreadLocal threadLocal = new InheritableThreadLocal();       
threadLocal.set("帅得一匹");    
Thread t = new Thread() {        
    @Override        
    public void run() {            
      super.run();            
      Log.i( "张三帅么 =" + threadLocal.get());        
    }    
  };          
  t.start(); 
} 
//原理如下,如果父线程的itl不为空,就继承
public class Thread implements Runnable {
  ……
   if (inheritThreadLocals && parent.inheritableThreadLocals != null)
      this.inheritableThreadLocals=ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
  ……
}

为什么使用弱引用?

假设使用强引用,业务代码使用完ThreadLocal,但是ThreadLocalMap中的Key强引用了ThreadLocal,造成了ThreadLocal无法回收.在没有手动删除Entry而且当前线程一直运行的情况下,强引用链Thread Ref->currentThread->ThreadLocalMap->Entry会一直存在,Entry不会被回收但是又不会被用到,所以Entry内存泄露

同样是使用完了ThreadLocal,ThreadLocal Ref被回收,由于ThreadLocalMap持有ThreadLocal的弱引用,所以ThreadLocal可以被顺利GC,Entry的key=null,但是其value不会被回收,因此在使用完变量之后必须显示的remove方法删除对应的Entry

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值