今天腾讯内网在热火朝天地集体算账
共 8079字,需浏览 17分钟
·
2024-07-12 14:04
大家好,我是二哥呀。
在星球嘉宾 Jack 那里看到一条消息说,鹅厂内网正在热火朝天的集体算账:一是把年终奖的一部分提前摊入到 base 中,二是把房补也平均纳入到 base 当中。
这个薪酬调整说实话对于 base 低的小伙伴来说很有利,因为 base 高了意味着五险一金缴纳的更多了,每个月到手的也更多了,真正的落袋为安。
话说这公积金如果离职的话,也是可以提取的,相当于一笔不小的存款。当然了,腾讯的工作体感在所有的大厂里算是很友好的了,估计进了就不想走,除非(🤣)。
但对于 base 已经非常高的小伙伴来说,每个月的个人所得税交的就更多了,但我想既然挣的多,多纳点税也算是合情合理(反正我每个月也交不少)。
至于腾讯为什么做出这个调整,我想肯定是奔着为员工谋福利去的。说实话,这年头,肯为员工谋福利的好公司不多了,腾讯这波值得被点赞。星球里之前也有好几个球友拿到了腾讯的实习 offer,希望都稳稳转个正。
那今天我们就以《Java 面试指南》中收录的《腾讯面经同学 26 微信支付暑期实习》 面试为例,来看看腾讯的面试官都喜欢问哪些问题,好做到知彼知己百战不殆。
这次面试的整体难度感觉不大,三道算法题也是 LeetCode 上 100 题中笔刷的题目,八股的话也都是非常常见的,属于送分八股(🤣)。
1、二哥的 Linux 速查备忘手册.pdf 下载 2、三分恶面渣逆袭在线版:https://javabetter.cn/sidebar/sanfene/nixi.html
腾讯微信支付面经
说一说项目的亮点。
PmHub 是一套基于 SpringCloud Alibaba & LLM 的智能项目管理系统,目前拆分了用户、流程、项目管理、认证等 4 个微服务,这个项目的亮点很多,比如说:
-
Gateway 实现自定义网关统一鉴权统计接口调用时间 -
使用 Redis+Lua 基于令牌桶实现限流 -
使用 RocketMQ 实现审批消息异步解耦 -
集成 OpenFeign+Sentinel 实现服务降级和网关流量控制 -
集成 Redis 分布式锁保障流程状态更新 -
通过分布式事务 Seata 保证任务审批状态一致性 -
自定义注解+AOP 实现服务接口鉴权和内部认证 -
整合 TTL 缓存用户数据 -
如何用 Docker 容器化部署项目 -
使用 Skywalking 监控项目性能 -
采用 Cache Aside 模式保证缓存和数据库一致性
JVM垃圾删除
垃圾回收(Garbage Collection,GC),顾名思义就是释放垃圾占用的空间,防止内存爆掉。有效的使用可以使用的内存,对内存堆中已经死亡的或者长时间没有使用的对象进行清除和回收。
JVM 在做垃圾回收之前,需要先搞清楚什么是垃圾,什么不是垃圾,那么就需要一种垃圾判断算法,通常有引用计数算法、可达性分析算法。
在确定了哪些垃圾可以被回收后,垃圾收集器要做的事情就是进行垃圾回收,如何高效地进行垃圾回收呢?
可以采用标记清除算法、复制算法、标记整理算法、分代收集算法等。
JVM 提供了多种垃圾回收器,包括 CMS GC、G1 GC、ZGC 等,不同的垃圾回收器采用的垃圾收集算法也不同,因此适用于不同的场景和需求。
CMS 是第一个关注 GC 停顿时间(STW 的时间)的垃圾收集器,JDK 1.5 时引入,JDK9 被标记弃用,JDK14 被移除。
G1(Garbage-First Garbage Collector)在 JDK 1.7 时引入,在 JDK 9 时取代 CMS 成为了默认的垃圾收集器。
ZGC 是 JDK11 推出的一款低延迟垃圾收集器,适用于大内存低延迟服务的内存管理和回收,在 128G 的大堆下,最大停顿时间才 1.68 ms,性能远胜于 G1 和 CMS。
https的加密技术
使用 HTTPS 主要是为了解决 HTTP 传输过程中的一些安全问题,因为 HTTP 是明文传输,所以 HTTPS 在 HTTP 的基础上加入了 SSL/TLS 协议。
SSL(安全套接字)/TLS(传输层安全)协议可以用来加密通信内容,保证通信过程中的数据不被窃取和篡改。整个加密过程主要涉及两种类型的加密方法:
-
非对称加密:服务器向客户端发送公钥,然后客户端用公钥加密自己的随机密钥,也就是会话密钥,发送给服务器,服务器用私钥解密,得到会话密钥。 -
然后双方用会话密钥加密通信内容。
客户端会通过数字证书来验证服务器的身份,数字证书由 CA(证书权威机构)签发,包含了服务器的公钥、证书的颁发机构、证书的有效期等信息。
说一说常用的并发容器
我自己常用的并发容器主要有 ConcurrentHashMap、CopyOnWriteArrayList、LinkedBlockingQueue。
在 JDK 8 及以上版本中,ConcurrentHashMap 的实现进行了优化,不再使用分段锁,而是使用了一种更加精细化的锁——桶锁,以及 CAS 无锁算法。每个桶(Node 数组的每个元素)都可以独立地加锁,从而实现更高级别的并发访问。
同时,对于读操作,通常不需要加锁,可以直接读取,因为 ConcurrentHashMap 内部使用了 volatile 变量来保证内存可见性。
对于写操作,ConcurrentHashMap 使用 CAS 操作来实现无锁的更新,这是一种乐观锁的实现,因为它假设没有冲突发生,在实际更新数据时才检查是否有其他线程在尝试修改数据,如果有,采用悲观的锁策略,如 synchronized 代码块来保证数据的一致性。
CopyOnWriteArrayList 是一个线程安全的 ArrayList,它遵循写时复制(Copy-On-Write)的原则,即在写操作时,会先复制一个新的数组,然后在新的数组上进行写操作,写完之后再将原数组引用指向新数组。
这样,读操作总是在一个不变的数组版本上进行的,就不需要同步了。
ArrayBlockingQueue 是一个基于数组的有界阻塞队列,采用 ReentrantLock 锁来实现线程的互斥,而 ReentrantLock 底层采用的是 AQS 实现的队列同步,线程的阻塞调用 LockSupport.park 实现,唤醒调用 LockSupport.unpark 实现。
算法题
LRU
这道题先不按照 leetcode 的解法来写,直接告诉大家我们可以使用 LinkedHashMap 来实现 LRU 缓存,LRU 是 Least Recently Used 的缩写,即最近最少使用,是一种常用的页面置换算法,选择最近最久未使用的页面予以淘汰。
public class MyLinkedHashMap<K, V> extends LinkedHashMap<K, V> {
private static final int MAX_ENTRIES = 5; // 表示 MyLinkedHashMap 中最多存储的键值对数量
public MyLinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder) {
super(initialCapacity, loadFactor, accessOrder);
}
protected boolean removeEldestEntry(Map.Entry eldest) {
return size() > MAX_ENTRIES;
}
}
MyLinkedHashMap 是一个自定义类,它继承了 LinkedHashMap,并且重写了 removeEldestEntry()
方法——使 Map 最多可容纳 5 个元素,超出后就淘汰。
我们来测试一下。
MyLinkedHashMap<String,String> map = new MyLinkedHashMap<>(16,0.75f,true);
map.put("沉", "沉默王二");
map.put("默", "沉默王二");
map.put("王", "沉默王二");
map.put("二", "沉默王二");
map.put("一枚有趣的程序员", "一枚有趣的程序员");
System.out.println(map);
map.put("一枚有颜值的程序员", "一枚有颜值的程序员");
System.out.println(map);
map.put("一枚有才华的程序员","一枚有才华的程序员");
System.out.println(map);
输出结果如下所示:
{沉=沉默王二, 默=沉默王二, 王=沉默王二, 二=沉默王二, 一枚有趣的程序员=一枚有趣的程序员}
{默=沉默王二, 王=沉默王二, 二=沉默王二, 一枚有趣的程序员=一枚有趣的程序员, 一枚有颜值的程序员=一枚有颜值的程序员}
{王=沉默王二, 二=沉默王二, 一枚有趣的程序员=一枚有趣的程序员, 一枚有颜值的程序员=一枚有颜值的程序员, 一枚有才华的程序员=一枚有才华的程序员}
沉=沉默王二
和 默=沉默王二
依次被淘汰出局。
接雨水
这道题我在《二哥的 LeetCode 刷题笔记》中给出详细的题解,这里就先贴个图作证。
删除链表中倒数第n个节点。
这是 LeetCode 的第 19 题,我这里直接给出题解,也是直接能 beat 100% 的:
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
// 创建一个虚拟头节点,简化边界条件处理
ListNode dummy = new ListNode(0);
dummy.next = head;
// 第一次遍历,计算链表的总长度
int length = 0;
ListNode current = head;
while (current != null) {
length++;
current = current.next;
}
// 设置长度为到达要删除的节点的前一个节点
int index = length - n;
current = dummy;
// 第二次遍历,找到要删除的节点的前一个节点
for (int i = 0; i < index; i++) {
current = current.next;
}
// 删除节点,即跳过要删除的节点
current.next = current.next.next;
return dummy.next;
}
}
内容来源
-
星球嘉宾三分恶的面渣逆袭:https://javabetter.cn/sidebar/sanfene/nixi.html -
二哥的 Java 进阶之路(GitHub 已有 12000+star):https://javabetter.cn
ending
一个人可以走得很快,但一群人才能走得更远。二哥的编程星球已经有 5700 多名球友加入了,如果你也需要一个良好的学习环境,戳链接 🔗 加入我们吧。这是一个编程学习指南 + Java 项目实战 + LeetCode 刷题的私密圈子,你可以阅读星球专栏、向二哥提问、帮你制定学习计划、和球友一起打卡成长。
两个置顶帖「球友必看」和「知识图谱」里已经沉淀了非常多优质的学习资源,相信能帮助你走的更快、更稳、更远。
欢迎点击左下角阅读原文了解二哥的编程星球,这可能是你学习求职路上最有含金量的一次点击。
最后,把二哥的座右铭送给大家:没有什么使我停留——除了目的,纵然岸旁有玫瑰、有绿荫、有宁静的港湾,我是不系之舟。共勉 💪。