并发编程最新2021年面试题附答案解析,大汇总

1、生产上如何配置垃圾收集器的?

首先是内存大小问题,基本上每一个内存区域我都会设置一个上限,来避免溢出问题,比如元空间。通常,堆空间我会设置成操作系统的2/3(这是想给其他进程和操作系统预留一些时间),超过8GB的堆优先选用G1。

接下来,我会对JVM进行初步优化。比如根据老年代的对象提升速度,来调整年轻代和老年代之间的比例。

再接下来,就是专项优化,主要判断的依据就是系统容量、访问延迟、吞吐量等。我们的服务是高并发的,所以对STW的时间非常敏感。

我会通过记录详细的GC日志,来找到这个瓶颈点,借用gceasy(重点)这样的日志分析工具,很容易定位到问题。之所以选择采用工具,是因为gc日志看起来实在是太麻烦了,gceasy号称是AI学习分析问题,可视化做的较好。

                                               加端端老师免费领取更多编程资料

2、对象的访问定位有哪几种方式?

建立对象就是为了使用对象,我们的Java程序通过栈上的 reference 数据来操作堆上的具体对象。对象的访问方式有虚拟机实现而定,目前主流的访问方式有使用句柄和直接指针2种:

句柄:如果使用句柄的话,那么Java堆中将会划分出一块内存来作为句柄池,reference 中存储的就是对象的句柄地址,而句柄中包含了对象实例数据与类型数据各自的具体地址信息。

直接指针:如果使用直接指针访问,那么 Java 堆对象的布局中就必须考虑如何放置访问类型数据的相关信息,而reference 中存储的直接就是对象的地址。

这两种对象访问方式各有优势。使用句柄来访问的最大好处是 reference 中存储的是稳定的句柄地址,在对象被移动时只会改变句柄中的实例数据指针,而 reference 本身不需要修改。使用直接指针访问方式最大的好处就是速度快,它节省了一次指针定位的时间开销。

3、invokedynamic 指令是干什么的?

Java 7 开始,新引入的字节码指令,可以实现一些动态类型语言的功能。Java 8 的 Lambda 表达式就是通过 invokedynamic 指令实现,使用方法句柄实现。

4、CAS的问题

1、 CAS容易造成ABA问题

一个线程a将数值改成了b,接着又改成了a,此时CAS认为是没有变化,其实是已经变化过了,而这个问题的解决方案可以使用版本号标识,每操作一次version加1。在java5中,已经提供了AtomicStampedReference来解决问题。

2、 不能保证代码块的原子性

CAS机制所保证的知识一个变量的原子性操作,而不能保证整个代码块的原子性。比如需要保证3个变量共同进行原子性的更新,就不得不使用synchronized了。

3、 CAS造成CPU利用率增加

之前说过了CAS里面是一个循环判断的过程,如果线程一直没有获取到状态,cpu资源会一直被占用。

5、讲讲什么情况下会出现内存溢出,内存泄漏?

内存泄漏的原因很简单:

1、 对象是可达的(一直被引用)

2、 但是对象不会被使用

常见的内存泄漏例子:

    public static void main(String[] args) {
        Set<Object> set = new HashSet<>();

        for (int i = 0; i < 10; i++) {
            Object object = new Object();
            set.add(object);

            // 设置为空,该对象不再使用
            object = null;
        }

        // 但是set集合中还维护object的引用,gc不会回收object对象
        System.out.println(set);
        System.out.println(set.size());
    }
}

输出结果

[java.lang.Object@74a14482, 
java.lang.Object@677327b6, 
java.lang.Object@6d6f6e28, 
java.lang.Object@4554617c, 
java.lang.Object@45ee12a7, 
java.lang.Object@1b6d3586, 
java.lang.Object@7f31245a,
java.lang.Object@135fbaa4,
java.lang.Object@1540e19d, 
java.lang.Object@14ae5a5]
10

Process finished with exit code 0

解决这个内存泄漏问题也很简单,将set设置为null,那就可以避免上述内存泄漏问题了。其他内存泄漏得一步一步分析了。

内存溢出的原因:

1、 内存泄露导致堆栈内存不断增大,从而引发内存溢出。

2、 大量的jar,class文件加载,装载类的空间不够,溢出

3、 操作大量的对象导致堆内存空间已经用满了,溢出

4、 nio直接操作内存,内存过大导致溢出

解决:

1、 查看程序是否存在内存泄漏的问题

2、 设置参数加大空间

3、 代码中是否存在死循环或循环产生过多重复的对象实体、

4、 查看是否使用了nio直接操作内存。

6、类的实例化顺序

                                               加端端老师免费领取更多编程资料

1、 父类静态成员和静态初始化块 ,按在代码中出现的顺序依次执行

2、 子类静态成员和静态初始化块 ,按在代码中出现的顺序依次执行

3、 父类实例成员和实例初始化块 ,按在代码中出现的顺序依次执行

4、 父类构造方法

5、 子类实例成员和实例初始化块 ,按在代码中出现的顺序依次执行

6、 子类构造方法

检验一下是不是真懂了:

public class Base {
    private String name = "博客:Soinice";

    public Base() {
        tellName();
        printName();
    }

    public void tellName() {
        System.out.println("Base tell name: " + name);
    }

    public void printName() {
        System.out.println("Base print name: " + name);
    }
}
public class Dervied extends Base {
    private String name = "Java3y";

    public Dervied() {
        tellName();
        printName();
    }

    @Override
    public void tellName() {
        System.out.println("Dervied tell name: " + name);
    }

    @Override
    public void printName() {
        System.out.println("Dervied print name: " + name);
    }

    public static void main(String[] args) {
        new Dervied();
    }
}

输出数据:

Dervied tell name: null
Dervied print name: null
Dervied tell name: Java3y
Dervied print name: Java3y

Process finished with exit code 0

第一次做错的同学点个赞,加个关注不过分吧(hahaha。

7、Serial 垃圾收集器(单线程、 复制算法)

Serial(英文连续) 是最基本垃圾收集器,使用复制算法,曾经是JDK1.3.1 之前新生代唯一的垃圾收集器。Serial 是一个单线程的收集器,它不但只会使用一个 CPU 或一条线程去完成垃圾收集工作,并且在进行垃圾收集的同时,必须暂停其他所有的工作线程,直到垃圾收集结束。

Serial 垃圾收集器虽然在收集垃圾过程中需要暂停所有其他的工作线程,但是它简单高效,对于限定单个 CPU 环境来说,没有线程交互的开销,可以获得最高的单线程垃圾收集效率,因此 Serial垃圾收集器依然是 java 虚拟机运行在 Client 模式下默认的新生代垃圾收集器。

8、怎么唤醒一个阻塞的线程

如果线程是因为调用了wait()、sleep()或者join()方法而导致的阻塞,可以中断线程,并且通过抛出InterruptedException来唤醒它;如果线程遇到了IO阻塞,无能为力,因为IO是操作系统实现的,Java代码并没有办法直接接触到操作系统。

9、什么是阻塞式方法?

阻塞式方法是指程序会一直等待该方法完成期间不做其他事情,ServerSocket的accept()方法就是一直等待客户端连接。这里的阻塞是指调用结果返回之前,当前线程会被挂起,直到得到结果之后才会返回。此外,还有异步和非阻塞式方法在任务完成前就返回。

10、并发队列的常用方法

不管是那种列队,是那个类,当是他们使用的方法都是差不多的

方法名描述
add()在不超出队列长度的情况下插入元素,可以立即执行,成功返回true,如果队列满了就抛出异常。
offer()在不超出队列长度的情况下插入元素的时候则可以立即在队列的尾部插入指定元素,成功时返回true,如果此队列已满,则返回false。
put()插入元素的时候,如果队列满了就进行等待,直到队列可用。
take()从队列中获取值,如果队列中没有值,线程会一直阻塞,直到队列中有值,并且该方法取得了该值。
poll(long timeout, TimeUnit unit)在给定的时间里,从队列中获取值,如果没有取到会抛出异常。
remainingCapacity()获取队列中剩余的空间。
remove(Object o)从队列中移除指定的值。
contains(Object o)判断队列中是否拥有该值。
drainTo(Collection c)将队列中值,全部移除,并发设置到给定的集合中。

11、工作中常用的 JVM 配置参数有哪些?

12、怎样通过 Java 程序来判断 JVM 是 32 位 还是 64位?

13、newFixedThreadPool

14、Executors类是什么?

15、什么是双亲委派机制?

16、说说自己是怎么使用 synchronized 关键字,在项目中用到了吗

17、介绍一下 JVM 中垃圾收集器有哪些? 他们特点分别是什么?

18、为什么Thread类的sleep()和yield ()方法是静态的?

19、如何避免线程死锁

20、分代收集算法

21、多线程同步有哪几种方法?

22、什么是Executors?

23、谈谈永久代

24、对于JDK自带的监控和性能分析工具用过哪些?

25、Serial Old 收集器(单线程标记整理算法 )

26、四种线程池的创建:

27、线程池作用?

28、什么是堆

29、MinorGC、MajorGC、FullGC 什么时候发生?

30、简述Java的对象结构

31、JRE、JDK、JVM 及 JIT 之间有什么不同?

32、实现可见性的方法有哪些?

                                               加端端老师免费领取更多编程资料

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值