面试精选

跟谁学一面

2021-06-25  本文已影响0人  王勇1024

自定义缓存设计方案(LRU)

常用缓存淘汰算法

内存的淘汰机制主要有以下几种:
1、FIFO (First In, First Out)
先进先出算法
2、LFU (Least Frequently Used)
最不经常使用算法
3、LRU (Least Recently Used)
最近最少使用算法

实现思路

链表+HashMap

**增强的LinkedHashMap

LinkedHashMap结构示意图

LinkedHashMap 构造方法

    public LinkedHashMap() {
        // 调用HashMap的构造方法,其实就是初始化Entry[] table
        super();
        // 这里是指是否基于访问排序,默认为false
        accessOrder = false;
    }

Sprint Cloud 组件的自定义实现

Feign

  1. Https的支持
  2. 实现本地访问K8s内的服务发现
  3. 自定义HttpMessageConverter,用于解决消息头部中Content-Type=text/plain;charset=UTF-8导致解码失败问题

Zuul

  1. 与数据库结合实现动态路由和访问过滤

Admin

  1. 重定向指标输出路径到公司监控平台

其他

  1. 结合Apollo配置中心实现配置实时更新
  2. 结合使用场景实现大量默认配置,尽量减少配置数量
  3. 自定义骨架,降低模块创建成本

堆外内存的管理

堆外内存有什么优势?

减少垃圾回收:因为垃圾回收会对其他的应用产生影响
加快了复制的速度:堆内在flush到远程时,会先复制到直接内存(非堆内存),然后再发送;而存储在堆外内存相当于省略掉了这个工作
可以扩展至更大的内存空间。比如超过1TB甚至比主存还大的空间;

使用堆外内存的缺点

堆外内存的缺点就是内存难以控制,使用了堆外内存就间接失去了JVM管理内存的可行性,需要自己管理,当发生内存溢出时排查起来非常困难。

申请堆外内存

# 方法一(推荐)
ByteBuffer.allocateDirect(10 * 1024 * 1024)
# 方法二(不推荐)
unsafe.allocateMemory(size)

堆外内存回收

参考:JVM——堆外内存详解

JDK中使用DirectByteBuffer对象来表示堆外内存,每个DirectByteBuffer对象在初始化时,都会创建一个对应的Cleaner对象,这个Cleaner对象会在合适的时候执行unsafe.freeMemory(address),从而回收这块堆外内存。

当初始化一块堆外内存时,对象的引用关系如下:


image

其中firstCleaner的静态变量,Cleaner对象在初始化时会被添加到Clener链表中,和first形成引用关系,ReferenceQueue是用来保存需要回收的Cleaner对象。

如果该DirectByteBuffer对象在一次GC中被回收了,即

image

此时,只有Cleaner对象唯一保存了堆外内存的数据(开始地址、大小和容量),在下一次FGC时,把该Cleaner对象放入到ReferenceQueue中,并触发clean方法。

Cleaner对象的clean方法主要有两个作用:

  1. 把自身从Cleaner链表删除,从而在下次GC时能够被回收
  2. 释放堆外内存

在初始化DirectByteBuffer对象时,会自动去判断,如果堆外内存的环境很友好,那么就申请堆外内存;如果当前堆外内存的条件很苛刻时(即有很多无效内存没有得到释放),这时候就会主动调用System.gc()强制执行FGC,从而释放那些无效内存。

为了避免这种悲剧的发生,也可以通过-XX:MaxDirectMemorySize来指定最大的堆外内存大小,当使用达到了阈值的时候将调用System.gc来做一次full gc,以此来回收掉没有被使用的堆外内存。

两个线程交叉打印1和2

// 打印线程
public class Printer implements Runnable {
    private String val;
    private AtomicBoolean flag;
    private boolean targetFlag;

    public Printer(String _val, AtomicBoolean _flag, boolean _targetFlag) {
        this.val = _val;
        this.flag = _flag;
        this.targetFlag = _targetFlag;
    }

    @Override
    public void run() {
        while(true) {
            boolean currentFlag = this.flag.get();
            if(currentFlag == this.targetFlag) {
                synchorized(Printer.class) {
                    if(currentFlag == this.targetFlag) {
                        System.out.println(this.val);
                        // flag 置成相反的值
                        this.flag.set(!currentFlag);
                    }
                }
            }
        }
    }
}



// 主线程
public class Main{
    private static AtomicBoolean flag = new AtomicBoolean(false);

    public static void main(String[] args) {
       Thread printer1 = new Thread(new Printer("1", flag, false));
       Thread printer2 = new Thread(new Printer("2", flag, true));
       printer1.start();
       printer2.start();
       Thread.currentThread().join();
    }
}
上一篇 下一篇

猜你喜欢

热点阅读