社招|oppo Java岗社招面经

社招|oppo Java岗社招面经
最新回答
酱紫—喵

2021-08-19 11:42:58

OPPO Java岗社招面经问题解答如下

  1. 讲一下Java的虚拟机

    核心组成:Java虚拟机(JVM)由类加载子系统、运行时数据区(方法区、堆、虚拟机栈、本地方法栈、程序计数器)、执行引擎和本地方法接口组成。

    关键功能:负责加载.class文件、管理内存、执行字节码、提供垃圾回收机制,并实现跨平台运行。

    内存模型:堆(对象实例存储)、方法区(类元数据)、栈(线程私有,存储局部变量表等)。

  2. 如何让虚拟机中的方法区直接爆满?

    动态生成大量类:通过反射或字节码操作(如ASM、CGLIB)在运行时生成大量类,填满方法区的永久代(JDK 8前)或元空间(JDK 8+)。

    加载超大JAR包:引入包含海量类的第三方库,或通过自定义类加载器重复加载类。

    极端场景:使用工具生成重复但不同的类定义(如通过循环生成类名不同的类)。

  3. 讲一下Java的垃圾回收机制

    分代收集:堆分为新生代(Eden、Survivor区)和老年代,采用不同回收策略(如新生代用复制算法,老年代用标记-清除或标记-整理)。

    触发条件:新生代Eden区满时触发Minor GC;老年代空间不足或调用System.gc()时触发Full GC。

    常见算法

    标记-清除:标记无用对象后直接清除,产生内存碎片。

    复制算法:将存活对象复制到另一块内存,适合新生代。

    标记-整理:移动存活对象至一端,避免碎片化,适合老年代。

    收集器类型:Serial、Parallel、CMS、G1(分区收集,兼顾吞吐量和低延迟)。

  4. 把Java中的容器类都讲一下

    Collection接口

    List:有序可重复,如ArrayList(动态数组)、LinkedList(双向链表)、Vector(线程安全)。

    Set:无序不可重复,如HashSet(哈希表)、TreeSet(红黑树排序)、LinkedHashSet(保持插入顺序)。

    Queue:队列,如LinkedList(双端队列)、PriorityQueue(优先级队列)。

    Map接口:键值对存储,如HashMap(哈希表)、TreeMap(红黑树排序)、LinkedHashMap(保持插入顺序)、ConcurrentHashMap(线程安全)。

    工具类:Collections(排序、同步化)、Arrays(数组操作)。

  5. Java中的锁是怎么实现的?

    synchronized

    对象锁:修饰方法或代码块,基于对象头中的Mark Word实现,通过monitorenter/monitorexit指令控制。

    类锁:修饰静态方法,锁为Class对象。

    Lock接口:如ReentrantLock(可重入锁)、ReadWriteLock(读写锁),通过CAS(Compare-And-Swap)和AQS(AbstractQueuedSynchronizer)框架实现。

    其他锁:Semaphore(信号量)、CountDownLatch(倒计时锁)、CyclicBarrier(循环屏障)。

  6. 引用计数法有什么缺点?

    计数器开销大:每次对象引用变更需更新计数器,高频操作影响性能。

    内存占用高:计数器需足够位数(如32位系统需32位计数器),可能占用对象内存的1/3。

    无法处理循环引用:如对象A引用B,B引用A,计数器始终≥1,导致内存泄漏。

  7. 说一下TCP的三次握手和四次挥手

    三次握手

    客户端发送SYN包(seq=x)到服务器,进入SYN_SENT状态。

    服务器回复SYN+ACK包(seq=y, ack=x+1),进入SYN_RCVD状态。

    客户端发送ACK包(ack=y+1),连接建立,双方进入ESTABLISHED状态。

    四次挥手

    客户端发送FIN包(seq=u),进入FIN_WAIT_1状态。

    服务器回复ACK包(ack=u+1),进入CLOSE_WAIT状态。

    服务器发送FIN包(seq=v),进入LAST_ACK状态。

    客户端回复ACK包(ack=v+1),进入TIME_WAIT状态,等待2MSL后关闭。

  8. 为什么挥手时有个TIME_WAIT?即2MSL

    确保最后一个ACK到达:防止服务器未收到ACK而重传FIN包,客户端需保留足够时间(2MSL,最大段生命周期的两倍)等待重传。

    避免新旧连接混淆:确保网络中残留的旧连接数据包失效,避免影响新连接。

  9. 浏览器输入URL到页面出来的流程

    缓存检查:依次查询浏览器缓存、系统缓存、路由器缓存。

    DNS解析:将域名转换为IP地址(可能涉及递归查询或迭代查询)。

    TCP连接:通过三次握手建立连接。

    HTTP请求:发送请求数据包(如GET/POST)。

    服务器处理:返回响应数据(如HTML、CSS、JS)。

    页面渲染:解析HTML生成DOM树,加载CSS构建渲染树,执行JS交互,最终渲染页面。

    后续交互:可能通过AJAX动态加载数据。

  10. 操作系统中的死锁怎么形成的,怎么预防死锁?

    形成条件

    互斥:资源独占使用。

    占有并等待:持有资源同时请求其他资源。

    非抢占:资源不能被强制释放。

    循环等待:存在资源等待环路。

    预防方法

    破坏占有并等待:一次性申请所有资源。

    破坏非抢占:允许抢占资源。

    破坏循环等待:按顺序申请资源。

    避免死锁:通过银行家算法等动态检测资源分配状态。

  11. 进程和线程有什么区别?

    资源分配:进程是资源分配的基本单位,线程是CPU调度的基本单位。

    开销:进程切换开销大(需切换内存空间),线程切换开销小(共享同一进程资源)。

    通信:进程间通信需IPC(如管道、消息队列),线程间可直接共享内存。

    安全性:进程间独立性强,线程间共享数据需同步机制(如锁)。

  12. 线程的几种状态

    新建(NEW):线程创建但未启动。

    可运行(RUNNABLE):包括就绪(等待CPU调度)和运行中状态。

    阻塞(BLOCKED):等待获取锁或I/O操作。

    等待(WAITING):调用Object.wait()或Thread.join()进入无限期等待。

    超时等待(TIMED_WAITING):调用Thread.sleep()或带超时的wait()。

    终止(TERMINATED):线程执行完毕或异常退出。

  13. 线程池用过没,怎么使用,流程是什么?

    核心组件

    核心线程数(corePoolSize):常驻线程数量。

    最大线程数(maximumPoolSize):线程池允许的最大线程数。

    工作队列(BlockingQueue):存放待执行任务的队列。

    线程工厂(ThreadFactory):创建线程的工厂。

    拒绝策略(RejectedExecutionHandler):队列满时的处理策略(如抛异常、丢弃任务)。

    使用流程

    创建线程池(如Executors.newFixedThreadPool(int nThreads))。

    提交任务(execute()或submit())。

    线程池管理任务执行(核心线程不足时创建新线程,队列满时触发拒绝策略)。

    关闭线程池(shutdown()或shutdownNow())。

  14. 创建线程有哪些方法,有什么区别,你一般怎么创建?

    继承Thread类:重写run()方法,通过start()启动。

    实现Runnable接口:实现run()方法,通过Thread(Runnable target)构造线程。

    实现Callable接口:实现call()方法(可返回结果),通过FutureTask和ExecutorService执行。

    线程池:通过Executors工具类创建线程池,复用线程资源。

    推荐方式:优先使用线程池(避免频繁创建销毁线程的开销),其次使用Runnable或Callable接口(避免单继承限制)。