面试

1. Java 中的引用有哪几种?

(1) 强引用:无论内存是否足够,不会回收,它在程序中普遍存在,如:

Object object = new Object();String string = "hello";

(2) 软引用:软引用是用来描述一些有用但并不是必需的对象,在 Java 中用 java.lang.ref.SoftReference 类表示。对于软引用,在内存不足时,回收该引用关联的对象。

SoftReference<String> sr = new SoftReference<String>(new String("hello"));

(3) 弱引用:用来描述非必需的对象,当 JVM 进行垃圾回收时,无论内存是否充足,都会回收该引用关联的对象。在 Java 中,用 java.lang.ref.WeakReference 类来表示。

WeakReference<String> sr = new WeakReference<String>(new String("hello"));

(4) 虚引用:虚引用和前面的软引用、弱引用不同,它并不影响对象的生命周期,任何时候都可能被垃圾回收器回收。在 Java 中用 java.lang.ref.PhantomReference 类表示。

ReferenceQueue<String> queue = new ReferenceQueue<String>();
PhantomReference<String> pr = new PhantomReference<String>(new String("hello"), queue);

2. Java 中的 threadlocal 是怎么用的?threadlocal 中的内部实现是什么样的?

  • ThreadLocal 称为线程本地变量,它是一种特殊的线程绑定机制,将变量与线程绑定在一起,为每一个线程维护一个独立的变量副本。通过 ThreadLocal 可以将对象的可见范围限制在同一个线程内,即将对象的作用范围限制在一个线程上下文中,并且可以在线程周期任意范围内获取到。

  • 每一个 Thread 对象都有一个 ThreadLocalMap 对象,ThreadLocal 仅仅是一个入口,它的 get 或 set 操作是获取当前线程的 ThreadLocalMap 引用,然后通过 key 在 ThreadLocalMap 中查找对应的 value 值。

3. Java 中的 final 关键字在多线程的中有什么含义?

  • 保证在构造函数中的变量设置,对其它线程来说都是可见的。而且对于通过 final 变量到达的任意变量,对其他线程也是可见的。

4. 说说 nio 的架构,为什么变快了,说说 select 和 buffer 都是怎么用的?

  • nio 做了内存映射,少了一次用户空间和系统空间之间的拷贝

  • nio 是异步的,触发式响应,是非阻塞的,充分利用了系统资源,提高了效率

  • select(选择器) 在 Java nio 中能够检测一个到多个通道,并能够知道是否为如读写事件做好准备的组件,这样一个单独线程就可以管理多个 channels

  • buffer 是一个缓存区,其用来将 channel 中的数据存储起来

5. nio 应用到了 linux 中的什么特性?

  • nio 是 linux 一种同步非阻塞 IO 模式

6. 在 nio 中,什么情况会存在内存泄漏?

  • 在 nio 程序使用 ByteBuffer 来读取或者写入数据时,可以使用 allocate 和 allocateDirect 来分配缓存,前者是分配 JVM 堆内存,属于 GC 管辖范围,由于需要拷贝所以速度相对较慢;第二种方式是分配 OS 本地内存,不属于 GC 管辖范围,由于不需要内存拷贝所以速度相对较快。本地内存在 Java 中有一个对应的包装类 DirectByteBuffer,该类属于 Java 类,适当的时候会被 GC 回收,当它被回收前会调用本地方法把直接内存给释放了,所以本地内存可以随 DirectByteBuffer 对象被回收而自动回收;但是如果不断分配本地内存,堆内存很少使用,那么 JVM 就不需要执行 GC,DirectByteBuffer 对象们就不会被回收,这时候堆内存充足,但本地内存可能已经使用光了,再次尝试分配本地内存就会出现 OutOfMemoryError。

7. JVM 垃圾回收分为哪些种类?每一种都是怎么去实现的?讲述一下 G1 的回收策略?

  • Java 有四种类型的垃圾回收器:串行垃圾回收器、并行垃圾回收器、并发标记扫描垃圾回收器、G1 垃圾回收器

    • 串行垃圾回收器:通过持有应用程序所有的线程进行工作,它为单线程环境设计,只使用一个单独的线程进行垃圾回收,通过冻结所有应用程序进行工作,所以可能不适合服务器环境。它最适合的是简单的命令行程序,通过 JVM 参数 -XX:+UseSerialGC 可以使用串行垃圾回收器。

    • 并行垃圾回收器:并行垃圾回收器也叫做 throughput collector,它是 JVM 的默认垃圾回收器。与串行垃圾回收器不同,它使用多线程进行垃圾回收。在垃圾回收的时候会冻结所有的应用程序线程。

    • 并发标记扫描垃圾回收器:使用多线程扫描堆内存,标记需要清理的实例并且清理被标记过的实例。相比并行垃圾回收器,并发标记扫描垃圾回收器使用更多的 CPU 来确保程序的吞吐量。通过 JVM 参数 -XX:+UseParNewGC 打开并发标记扫描垃圾回收器。

    • G1 垃圾回收器:G1 垃圾回收器适用于堆内存很大的情况,它将堆内存分割成不同的区域,并且并发的对其进行垃圾回收。G1 也可以在回收内存之后对剩余的堆内存空间进行压缩,并发扫描标记垃圾回收器在 STW 情况下压缩内存。G1 垃圾回收会优先选择第一块垃圾最多的区域。通过 JVM 参数 -XX:UseG1GC 使用 G1 垃圾回收器。

8. JVM 中的启动参数分为哪些种类,都是做什么的?JVM 的监控怎么做?实际项目上线以后的监控怎么做?

  • JVM 的启动参数分为三类:

    • 标准参数 (-):所有的 JVM 实现都必须实现这些参数的功能,而且向后兼容;

    • 非标准参数 (-X):指的是 JVM 底层的一些配置参数,生成环境中往往为了提高性能要调整这些参数。

    • 非Stable参数 (-XX),这类参数在 JVM 中是不稳定的,不适合日常使用,后续也有可能会在没有通知的情况下就直接取消了,需要慎重使用。

  • JVM 主要启动参数作用如下:

    • -Xms :设置 JVM 内存的初始大小

    • -Xmx :设置 JVM 内存的最大值

    • -Xmn :设置新域的大小

    • -Xss :设置每个线程的堆栈大小

    • -XX:NewRatio :设置新域与旧域之比

    • -XX:NewSize :设置新域的初始值

    • -XX:MaxNewSize :设置新域的最大值

    • -XX:MaxPermSize :设置永久域的最大值

    • -XX:SurvivorRatio=n :设置新域中 Eden 区与两个 Survivor 区的比值。

  • JVM 性能监控工具

    • JDK 命令行工具

    • jps :虚拟机进程状况工具

    • jstat :虚拟机统计信息监视工具

    • jinfo :Java 配置信息工具

    • jmap :Java 内存映像工具

    • jhat :虚拟机堆存储快照分析工具

    • jstack :Java 堆栈跟踪工具

    • JDK 的可视化工具

    • JConsole

    • VisualVM

    • 其他

    • Eclipse Memory Analyer

9. JVM 中的内存结构分为哪些方面?

  • 栈:存放局部变量

  • 堆:存放所有 new 出来的东西

  • 方法区:被虚拟机加载的类信息、常量、静态变量等

  • 程序技术器(和系统相关)

  • 本地方法栈

10. 栈空间结构是怎么样的?每个线程只有一个栈吗?

  • Java 中每个线程都有着虚拟机栈,而虚拟机栈中的元素是一个个的栈帧,栈帧使用于支持虚拟机进行方法调用和方法执行的数据结构。每一个方法从调用开始到执行完成的过程都可以看作是一个栈帧于虚拟机栈中从入栈到出栈的过程。

  • 栈空间具体结构如下图所示:

11. 堆内存为什么要设计分代?

  • 堆内存时虚拟机管理的内存中最大的一块,也是垃圾回收最频繁的一块区域,给堆内存分代则是为了提高对象内存分配和垃圾回收的效率。

12. ArrayList 的实现原理,如何测试 ArrayList 动态分配内存带来的内存、CPU 变化?

  • ArrayList 是用数组实现的,这个数组在内存中是连续的

  • 索引 ArrayList 时,速度比原生数组慢是因为你要用 get 方法,这是一个函数调用,而数组直接用下标访问,相当于直接操作内存地址,速度当然非常快

  • 新建 ArrayList 的时候,JVM 为其分配一个默认或指定大小的连续内存区域

  • 每次增加元素会检测容量,不足则创建新的连续内存区域,并将原来的内存区域数据复制到新的内存区域,然后再用 ArrayList 中引用原来封装的数组对象的引用变量引用到新的数组对象。

13. ArrayList 是不是线程安全的?怎么实现线程安全?

  • ArrayList 不是线程安全的,可以使用 Collections.synchronizedList() 保证线程安全,如:

List<Map<String, Object>> data = Collections.synchronizedList(new ArrayList<Map<String, Object>>());

14. synchronized 和 lock 有什么区别?

类别synchronizedLock  
层次上Java 的关键字,在 JVM 层面上是一个类 
锁的释放1、以获取锁的线程执行完同步代码,释放锁 2、线程执行发生异常,JVM 会让线程释放锁在 finally 中必须释放锁,不然容易造成线程死锁 
锁的获取假设 A 线程获得锁,B 线程等待。如果 A 线程阻塞,B 线程会一直等待分情况而定,Lock 有多个锁获取的方式,具体下面会说道,大致就是可以尝试获得锁,线程可以不用一直等待 
锁状态无法判断可以判断 
锁的类型可重入,不可中断,非公平可重入,可中断,可公平 
性能少量同步大量同步 

15. volatile 的作用,如果 volatile 修饰的对象经过了大量的写,会出现什么问题?

  • 如果一个基本变量被 volatile 修饰,编译器将不会把它保存到寄存器中,而是每一次都去访问内存中实际保存该变量的位置上。这一点就避免了没有 volatile 修饰的变量在多线程的读写中所产生的由于编译器优化所大致的灾难性问题,所以多线程中必须要共享的基本变量一定要加上 volatile 修饰符。

  • volatile 修饰的变量由于禁止优化,直接操作内存位置,因此大量的写严重影响效率。

16. String 的 + 和 StringBuilder 有什么区别?放在循环中会有什么问题?

  • String 是字符串常量,它的 + 操作会重新创建对象,而 StringBuilder 的内容可以修改

  • String 类型变量放在循环中操作会因为大量创建临时对象,严重影响效率,因此对字符串操作最好使用 StringBuilder。

17. 日志打印的过程中,使用 String 的 + 操作和使用占位符输出,对性能上有什么区别?

  • String 的 “+” 操作会创建很多临时对象,因此日志操作使用占位符格式化输出。

18. SimpleDateFormat 如果是一个全局变量的话,有什么问题?

  • SimpleDateFormat 不是线程安全的,在多线程环境中会有线程安全问题。

对应 Java 开发面试,还是考察对基础原理知识的了解程序,大部分都是Java虚拟机,还有就是对常见 API 实现原理,这就要求我们要多看看源代码了。

https://mp.weixin.qq.com/s/0W7-wAwonXLtV4D0uQBuxQ

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值