当前位置: 首页 > news >正文

桂林网站建设服务电话手机网站建设需求

桂林网站建设服务电话,手机网站建设需求,网站建设如何账务处理,网站图片怎么换OOM后JVM一定会退出吗?为什么? 文章来源:阿里一面:OOM后JVM一定会退出吗?为什么? OOM (OutOfMemoryError) 本身不会直接导致 JVM 退出。JVM 是否退出的唯一决定性因素是:是否还有任何非守护线…

OOM后JVM一定会退出吗?为什么?

文章来源:阿里一面:OOM后JVM一定会退出吗?为什么?

OOM (OutOfMemoryError) 本身不会直接导致 JVM 退出。JVM 是否退出的唯一决定性因素是:是否还有任何非守护线程(Non-Daemon Thread)在运行。 OOM 只是导致单个线程终止的一种错误,其影响力仅限于抛出该错误的线程。

关键概念解析

  1. JVM 退出条件
    JVM 的设计规范决定了其退出的时机:当所有非守护线程都终止时,JVM 才会正常退出

  2. 守护线程 (Daemon Thread) vs. 非守护线程 (Non-Daemon Thread)

    • 非守护线程:通常由应用程序创建的主线程(main)和工作线程。它们的生命周期独立于 JVM,只要还有一个非守护线程在运行,JVM 就会保持存活。例如:main 线程、线程池的核心线程(默认)。

    • 守护线程:为其他线程提供服务的后台线程(如垃圾回收线程)。它们的生存依赖于 JVM,当所有非守护线程结束时,JVM 会忽略并终止所有守护线程,然后退出。

  3. OOM (OutOfMemoryError):属于 Error,是 Throwable 的一种。它表示 Java 虚拟机因内存不足而无法继续执行操作。和所有 Throwable 一样,OOM 的影响范围是线程级的。如果一个线程抛出的 OOM 未被捕获,该线程会终止,但不会波及其他线程或直接导致 JVM 退出。

实验现象与分析 (来自原文)

  • 实验场景
    线程池中的一个工作线程(ThreadPool-Thread)因任务需要大量内存而触发 OOM。

  • 观察结果

    1. ThreadPool-Thread 线程终止,其状态变为 WAITING(实际上,更准确的描述可能是该线程因 OOM 终止,而从线程池的角度看,这个工作线程实例被移除,线程池可能会尝试创建新的线程,但受限于内存可能失败)。

    2. main 线程(非守护线程)依然在运行,持续打印“我还活着”。

    3. JVM 进程一直没有退出

  • 实验结论
    即使某个线程因 OOM 而终止,只要还存在至少一个活跃的非守护线程(如 main 线程或线程池中存活的核心线程),JVM 就不会退出。这完美印证了 JVM 的退出机制。

OOM 导致 JVM “间接退出” 的两种特殊场景

我们之所以常感觉“OOM 了程序就挂了”,其实是以下两种场景造成的错觉,而非 OOM 本身直接杀死 JVM。

场景机制根本原因
场景一:所有非守护线程均因 OOM 终止JVM 内所有非守护线程在申请内存时都抛出了未捕获的 OOM,导致所有非守护线程相继终止。满足了 JVM 的自发退出条件JVM 正常的生命周期结束。因为没有了非守护线程,JVM 自然退出。
场景二:被操作系统强制终止 (Linux OOM Killer)JVM 进程因内存泄漏或疯狂申请内存,占用了大量系统内存,触发了 Linux 内核的 OOM Killer 机制。内核会选择“最坏”的进程(通常是耗内存最多的 JVM 进程)并强制杀死(kill -9)。操作系统的自我保护行为。JVM 是被外部力量“杀死的”,它自身还未来得及处理或退出。(Windows 无此机制)

知识点总结与启示

  1. 线程隔离性:Java 中错误的传播是线程隔离的。一个线程的崩溃(如 OOM)不会直接导致整个进程崩溃。

  2. 线程池的健壮性:线程池的设计很好地体现了这一点。一个任务的异常(包括 OOM)只会导致执行该任务的工作线程受影响,而线程池本身(作为一组管理线程)和其他工作线程可以继续运行,接收新任务。

  3. 问题排查

    • 遇到 JVM 进程僵死(不退出也不工作),可以检查是否还有非守护线程在阻塞或循环等待。

    • 遇到 JVM 进程突然消失(特别是 Linux 环境下),可以查看 /var/log/messages 或使用 dmesg 命令检查是否有 OOM Killer 的日志记录。

  4. 设计考量:对于可能发生 OOM 的关键任务,应考虑捕获 OutOfMemoryError(或更通用的 Throwable),在线程终止前进行必要的日志记录和资源清理,避免整个应用处于一种“部分瘫痪”的僵死状态。

对比表格:普通认知 vs. 实际原理

普通认知实际原理
程序抛出 OOM,JVM 就会崩溃退出。OOM 只终止当前线程。JVM 退出与否取决于非守护线程是否全部终止。
OOM 是导致 JVM 退出的直接原因。OOM 是间接原因。它通过导致所有非守护线程终止来满足 JVM 的退出条件。
JVM 进程被杀死一定是代码bug。也可能是被Linux OOM Killer等外部机制强制终止。

关于“时间精度不一致导致解封日期错误”的问题分析

文章来源:时间设置的是23点59分59秒,数据库却存的是第二天00:00:00

一、 问题概述

  • 问题现象:运营反馈,用户被封禁2天后应解封,但系统提示的解封时间却是3天后。

  • 具体案例:6月16日封禁,预期解封时间为6月18日 23:59:59,但系统提示和数据库实际存储的却是6月19日 00:00:00。

  • 数据特征:数据库中近一半的数据正确(23:59:59),另一半错误(00:00:00),且问题随机出现。

二、 代码逻辑与初步分析

LocalDateTime currentTime = LocalDateTime.now();
LocalDateTime futureTime = currentTime.plus(2, ChronoUnit.DAYS); // 加2天
// 设置为当天的最后一秒
LocalDateTime resultTime = futureTime.withHour(23).withMinute(59).withSecond(59);BlackAccount entity = new BlackAccount();
// 关键转换:将LocalDateTime(纳秒精度)转换为java.util.Date(毫秒精度)
// 实体字段类型为Date,数据库是timestamp
entity.setDeblockTime(Date.from(resultTime.atZone(ZoneId.systemDefault()).toInstant()));
blackAccountService.save(entity);

初步排查:

  1. 排除代码覆盖:确认项目中设置解封时间的代码仅此一处。

  2. 询问AI:AI指出了夏令时(DST) 和时区转换的潜在风险,但根据问题数据“任何时间点都存在两种情况”的特征,排除了这两种系统性偏差的可能性。

三、 问题定位与复现

  • 排查方法:编写批量插入测试代码,模拟高频次的数据写入。

  • 复现结果:成功复现问题!插入100条数据中,约一半是预期的 23:59:59,另一半则变成了次日的 00:00:00

  • 根本原因

    • Java端精度LocalDateTime 的精度是纳秒withSecond(59) 后,其纳秒字段(Nano)是一个很大的随机数(例如 59.123456789秒)。

    • 转换损耗java.util.Date 的精度是毫秒。在调用 Date.from(...) 转换时,纳秒会被舍入到毫秒(除以1000000)。

    • 数据库精度:数据库 TIMESTAMP 字段的默认精度为秒。当毫秒值 >= 500 时,数据库存入时会进行四舍五入,进位到下一秒。

    • 问题链LocalDateTime(nanos) -> Date(millis) -> Database(seconds)。当纳秒转换后的毫秒数 >= 500ms 时,入库后秒数 +1,于是 23:59:59.500 就变成了 00:00:00

四、 解决方案

方案一(推荐):在代码中统一精度

// 修改前:
LocalDateTime resultTime = futureTime.withHour(23).withMinute(59).withSecond(59);
// 修改后:清空纳秒部分,确保毫秒和秒级精度稳定
LocalDateTime resultTime = futureTime.withHour(23).withMinute(59).withSecond(59).withNano(0);

方案二:调整数据库精度
将数据库 TIMESTAMP 字段的精度调整为至少毫秒级(如 TIMESTAMP(3)),使其与 Java 的 Date 对象精度匹配。但这可能影响现有数据库 schema。

五、 知识扩展与对比

1. Java 时间类特性

特性java.util.Date (旧)java.time.LocalDateTime (新)
精度毫秒 (ms)纳秒 (ns)
时区内部基于UTC,但行为不直观无时区,纯本地时间
可变性可变(非线程安全)不可变(线程安全)
API 易用性差(需与 Calendar 配合)优秀,方法链式调用
推荐场景旧系统维护所有新项目,用于表示本地日期时间

2. MySQL 日期时间类型

特性DATETIMETIMESTAMP
存储范围1000-01-01 到 9999-12-311970-01-01 到 2038-01-19 (2038问题)
时区处理不感知时区,存什么读什么感知时区,存入和读取时会自动转换为会话时区
存储空间8 字节4 字节(范围小的原因)
自动更新不支持(除非显式设置)支持 DEFAULT CURRENT_TIMESTAMP 和 ON UPDATE
推荐场景存储固定的时间点(如生日、订单创建时间)记录与时间线强相关的时间(如最后登录、更新时间)

3. 适用场景建议

  • Java 开发:优先使用 java.time 包(如 LocalDateTimeZonedDateTime)。

  • 数据库选择

    • 如果业务需要时区自动转换(如跨国应用),用 TIMESTAMP

    • 如果业务需要存储一个绝对不变的时间点(如活动开始时间),用 DATETIME

    • 如果时间需要超过 2038年,必须使用 DATETIME

亿级“微信步数”排行榜设计

文章来源:https://mp.weixin.qq.com/s/V2DoDhCMCyqSZO9TC_ru_w

设计一个支持上亿用户的步数排行榜系统,要求能实时查看好友间的排名。直接使用 MySQL,建表存储用户步数,查询时使用 ORDER BY steps DESC LIMIT ...。这个方案在亿级数据和高并发场景下会立刻崩溃。存在的挑战如下:

  • 写入地狱(海啸):亿级用户可能在相近时间点(如睡前)集中上报数据,产生极高的写入并发峰值。

  • 查询噩梦:对亿级数据表进行实时排序和分页操作,是数据库的噩梦,会导致 CPU 飙升和响应极慢。

  • 存储黑洞:数据量巨大(每月30亿条),直接使用 MySQL 存储成本高昂且效率低下。

  • 关系迷宫:排行榜是动态且个性化的,基于每个人的好友关系实时计算,无法预先生成一个全局排行榜。

核心设计思想:异步解耦、冷热分离、动静分离

第一板斧:写入流程 —— “异步解耦,削峰填谷”

用户App -> 业务服务 -> 消息队列(MQ) -> 消费服务 -> Redis

目标: 抵御写入海啸,保证系统稳定性和接口响应速度。

步骤解析:

  1. 用户上报:用户 App 将 {userId, steps, timestamp} 数据上报至业务服务(如 Java 服务)。

  2. 快速响应:业务服务不对数据做复杂处理,仅进行基本校验,然后立即将消息写入消息队列(如 Kafka/RocketMQ)。之后立刻返回成功给用户。MQ在此处扮演了“蓄水池”的角色,成功将瞬时流量削峰填谷。

  3. 异步消费:下游的消费服务以自己能承受的速度从 MQ 中消费消息。

  4. 更新排行榜:消费服务使用 Redis 的 ZADD 命令,将用户步数更新到当天的有序集合(ZSet)中。

    • Key: leaderboard:2025-09-12 (按日期区分)

    • Score: steps (步数)

    • MemberuserId

    • 优势ZADD 时间复杂度为 O(logN),性能极高,且能自动按分数排序。

 此方案使写入接口响应极快,后端处理能力与前台请求解耦,系统吞吐量取决于消费服务的处理能力,可以水平扩展。

第二板斧:查询流程 —— “动静分离,内存计算”

用户查询 -> 业务服务 -> [MySQL取好友列表] + [Redis取步数] -> 内存排序 -> 返回结果

目标: 实现好友排行榜的毫秒级实时查询。

核心概念:

  • 静:好友关系。变化不频繁,存储在 MySQL(需分库分表)。

  • 动:步数数据。实时变化,存储在 Redis

步骤解析:

  1. 查询关系:用户 A 请求排行榜时,业务服务先去 MySQL 中查询他的好友 ID 列表(例如 200 个)。

  2. 批量取数:业务服务拿到好友 ID 列表后,使用 Redis 的 Pipeline 或并发方式,一次性从当日的 ZSet Key 中批量获取这些好友的步数(Score)。绝对避免循环单个查询!

  3. 内存计算:此时服务端内存中只有约 200 条数据,对其进行排序(ORDER BY steps DESC)的计算开销极小,速度极快。

  4. 组装返回:将排序后的列表,补充上用户昵称、头像等信息(可从二级缓存如 Redis Cache 或 MySQL 中获取),返回给前端。

小结: 将耗时操作分解,“动”“静”数据分离获取,最终在内存中完成轻量级的合并计算,保证查询效率。

追问1:如何处理“几百万好友的大V用户”?

问题: 实时查询流程中,第1步从 MySQL 拉取百万好友ID列表和第2步从 Redis 获取百万分数,都会非常慢。

解决方案: 预计算 + 缓存降级

  • 为这类“热点用户”启动一个离线定时任务(例如每分钟一次)。

  • 任务提前为他计算好好友排行榜的前 N 名(如 Top 1000)。

  • 将计算结果直接缓存到一个特定的 Key 中(如 precompute:leaderboard:${大VuserId})。

  • 当大V查询时,直接返回这个预计算好的缓存结果,绕过实时计算流程。

追问2:Redis 挂了数据会不会丢?

解决方案: 高可用架构 + 数据恢复能力

  1. 高可用:线上 Redis 必须部署为主从复制 + 哨兵(Sentinel)模式集群模式。主节点宕机,从节点会自动切换为主,保证服务可用性。

  2. 数据可恢复:即使整个 Redis 集群宕机,数据也不会“彻底丢失”。

    • 原因:我们的数据源头是 MQ。通常 MQ 会设置消息保留时间(如 2-3 天)。

    • 恢复手段:可以编写一个应急恢复程序,从 MQ 中重新消费当天(或最近)的步数上报消息,即可将排行榜数据在新的 Redis 集群中完整重建,达到最终一致性。

一个TODO引发的P级故障与复盘

文章来源:https://mp.weixin.qq.com/s/AbY8pb0a74x3rsKP55zHVA

  • 时间:2021年,某S级“会员闪促”活动零点。

  • 现象:活动刚上线,促销服务集群(promotion-marketing)上百台机器CPU和Load垂直飙升,应用可用度暴跌至10%以下,活动入口全部因超时被降级。

  • 耗时:从故障发生到最终恢复,总共约30分钟

  • 影响:精心筹备的大促活动“上线即失踪”,对用户体验和GMV造成重大影响。

排查的过程:

  1. 第一阶段:无效的常规操作

    • 看日志:发现一些无关紧要的NPE(空指针异常),排除。

    • 怀疑死锁:分析线程快照(jstack),未发现死锁,排除。

    • 重启大法:短暂有效,但新流量进来后立即复发。

    • 紧急扩容:新扩机器同样迅速被高负载和GC拖垮,治标不治本。

  2. 第二阶段:深入肌体——找到关键线索

    • 保留现场:保留一台故障机,转储(dump)堆内存和线程栈。

    • 分析堆内存:发现老年代使用率极高,Full GC频繁。内存中驻留了大量与“万豪活动配置”相关的char[]数组,暗示有一个巨大的活动配置对象无法被回收。

    • 分析线程栈:发现大量线程(246个)处于RUNNABLE状态,且堆栈信息都卡在 com.alibaba.fastjson.toJSONString(...) 方法上。

    • 大胆假设:一个巨大的对象正在被疯狂、反复地序列化,这个CPU密集型操作耗尽了所有线程资源。

  3. 第三阶段:定位元凶——“一行好代码”

    • 根据线程栈定位到 XxxxxCacheManager.java 文件。

    • 发现一段上方标有 // TODO: 此处有性能风险,大促前需优化 注释的缓存写入代码。

public void updateActivityXxxCache(Long sellerId, List<XxxDO> xxxDOList) {for (int index = 0; index < 20; index++) { // 设计20个散列Key以分散读压力tairCache.put(..., JSON.toJSONString(xxxDOList), ...); // 在循环内序列化巨大对象!}
}
  • 根因分析

    • 缓存击穿:零点活动生效,缓存无数据,请求穿透到DB后回写缓存。

    • 循环序列化:回写时,将一个1-2MB的大对象在for循环内序列化了20次,成为“CPU绞肉机”。

    • 中间件被打爆:放大20倍的写流量(20 x 1MB)打爆了性能脆弱的Tair LDB缓存中间件,触发其限流。

    • 恶性循环:Tair限流导致写入耗时飙升,进一步拉长了“CPU绞肉机”的单次操作时间,最终占满所有HSF线程,服务雪崩。

    • 解决措施:紧急回滚了这段“循环序列化”的代码,集群恢复。

核心反思与工程法则

  1. 法则一:任何脱离了容量评估的“优化”,都是在“耍流氓”。

    • 教训:为解决“读压力”而设计的20个散列Key本是“优化”,但未评估“写放大”的代价,反而成为故障导火索。

    • 启示:任何技术方案的设计和优化,必须进行全面的、量化的容量评估(包括CPU、内存、网络、中间件负载等),心存敬畏,而非盲目“炫技”。

  2. 法则二:监控的终点,是“代码块耗时”。

    • 教训:拥有机器、接口、中间件监控,但缺乏方法级/代码块级的APM(应用性能监控),导致无法快速定位问题代码。

    • 启示:建设精细化的链路追踪和能力,能够快速定位到耗时的具体代码行,是提升排查效率的关键。

  3. 法则三:技术债,总会在你最想不到的时候“爆炸”。

    • 教训:文中使用的Tair LDB是一个老旧、无人维护的中间件,其性能脆弱性是隐藏的“技术债”,在极端流量下被引爆。

    • 启示:对于系统中存在的老旧组件、临时方案(TODO/FIXME)、破窗代码,必须定期梳理和偿还。技术债如同蟑螂,平时看不见,关键时刻致命。

http://www.dtcms.com/a/476414.html

相关文章:

  • 网站换友链平台什么叫网络营销目标市场
  • 做平面设计什么素材网站好使12306 网站谁做的
  • 请描述网站开发的一般流程图网站设计技能培训
  • 做网站编辑需要什么文凭做明星同款的网站
  • 平谷建站推广做网站需要知道什么软件
  • 网站树状型结构优化wordpress初始设置密码
  • 网站开发小程序青岛设计网站的公司哪家好
  • 中山网站建设品牌wordpress uc点赞
  • 在网上做效果图赚钱的网站中国航发网上电子商城网址
  • 网站开发服务器的选择中国建设银行官网招聘
  • 佛山建设银行网站wordpress易语言
  • 网站管理和维护的主要工作有哪些安卓项目开发
  • 如何选择网站域名网站开发工程师 能做什么
  • 企业网站上线中文 wordpress 主题
  • 百度seo推广计划类型包含哪些行业适合做seo
  • 高端企业网站设计成都高新区规划建设局网站
  • 网站打开慢的解决方法广州网站建设公司兴田德润可以不
  • 做电影网站需要注意什么软件wordpress插件密钥实现
  • 北碚区建设银行网站佛山百度seo点击软件
  • 自主做网站宝安区
  • 丽水市企业网站建设 微信营销 影视拍摄找营销推广团队
  • 电子商务网站推广策略专业网站建设定制公司
  • 自己怎么给网站做优化排名云电脑免费体验30天
  • 一个网站需要什么怎么在小程序里开店流程
  • 废旧回收做哪个网站好怎样做已有网站的编辑维护
  • 广西最优秀的品牌网站建设公司1688官网首页
  • 可以自己做装修效果图的网站重庆的汽车网站建设
  • 定制网站开发系统如何做软件类型的网站
  • 绍兴网站制作计划佛山网站建设方案咨询
  • 苏宁易购网站建设分析东莞阿里巴巴网站建设