去年,我一直在帮助新兴公司Instana创建一个Java代理,该代理可跟踪Java应用程序中的执行情况。 收集并结合此执行数据以生成用户请求以及系统所有者半球内服务之间的最终通信的跟踪。 这样,可以可视化非结构化通信,从而显着简化了由多个交互服务组成的分布式系统的操作。

为了生成这些跟踪,Java代理重写了读取外部请求或发起外部请求的所有代码。 显然,需要记录这些进入或离开系统的入口和出口,此外,还需要交换元数据以跨系统唯一地标识请求。 例如,当跟踪HTTP请求时,代理会添加一个包含唯一ID的标头,然后由接收服务器记录该ID作为请求来源的证明。 从广义上讲,它与Zipkin建模的相似,但是不需要用户更改代码。

在最简单的情况下,这种跟踪很容易实现。 多亏了我的库Byte Buddy ,该库可以完成繁重的工作,所有注入的代码都是用普通的旧Java编写的 ,然后在运行时使用Java工具API复制到相关的方法中。 例如,在检测Servlet时,我们知道只要调用service方法就进入JVM。 我们也知道,当此完全相同的方法退出时,条目已完成。 因此,只需在方法的开头和结尾添加一些代码,以将任何此类条目记录到VM进程中即可。 我的大部分工作是浏览许多Java库和框架,以增加对其通信方式的支持。 从Akka到Zookeeper,在过去的一年中,我遍历了整个Java生态系统。 我什至必须为所有服务器编写EJB! 而且我不得不理解Sun的CORBA实现。 (剧透:没有道理。)

但是,在跟踪异步执行时,事情确实很快变得更加困难。 如果一个线程接收到一个请求,但另一个线程内部对该请求进行了响应,则仅跟踪入口和出口不再足够。 因此,我们的代理还需要跟踪通过线程池, 派生联接任务或自定义并发框架进行的并发系统中的所有上下文切换。 同样,调试异步执行很困难,这对我们来说也是很多工作。 我认为我花在处理并发上的时间与记录入口和出口所花的时间一样多。

对垃圾收集的影响

但是这一切如何影响垃圾回收? 在实现性能监视器时,面临着一个权衡,即在解释虚拟机的工作与通过这样做导致该机器的工作之间进行权衡。 尽管大部分处理是在代理程序向其报告数据的监视器后端中完成的,但我们必须在与受监视的应用程序共享的Java进程中做最少的工作。 您已经可以猜到:通过分配对象,我们不可避免地会对VM的垃圾回收产生影响。 幸运的是,现代垃圾收集算法正在做得很好,并且通过主要避免对象分配以及通过自适应采样我们的跟踪工作,我们的代码更改的影响对于绝大多数用户而言可以忽略不计。 理想情况下,我们只消耗一些未使用的处理器周期来完成工作。 实际上,很少有应用程序能够充分发挥其全部处理潜力,而我们很乐意抓住其中的一小部分。

编写垃圾回收友好的应用程序通常不太困难。 显而易见,避免垃圾的最简单方法是完全避免对象分配。 但是,对象分配本身也不错。 分配内存是一项相当便宜的操作,并且由于任何处理器都拥有自己的分配缓冲区( 即所谓的TLAB),因此在从线程中仅分配一点内存时,我们不会强加不必要的同步。 如果对象仅存在于方法范围内,则JVM甚至可以完全擦除对象分配,就好像将对象的字段直接放入堆栈中一样。 但是,即使没有这种逃逸分析 ,也可以通过称为“ 年轻一代”收集器的特殊垃圾收集器来捕获短命对象,该收集器可以非常有效地进行处理。 坦白说,这就是我大多数对象的结局,因为我经常将代码的可读性视为逃避分析所提供的细微改进。 目前,逃逸分析Swift触及了边界。 但是,我希望将来的HotSpots能够得到改进,以在不更改我的代码的情况下获得两全其美。 手指交叉!

在编写Java程序时,我通常不会考虑对垃圾回收的影响,但是上述准则往往会体现在我的代码中。 对于我们大多数代理商而言,这一直很好。 我们正在运行大量示例应用程序和集成测试,以确保我们的代理具有良好的行为,并且在运行示例时,我也会关注GC。 在我们现代,使用飞行记录器和JIT手表等工具进行性能分析变得相当容易。

短命的相对性

在早期版本的代理中,有一天我注意到一个触发永久收集周期的应用程序,如果没有它,它就不会触发。 结果,收集暂停增加了许多。 但是,最终存留在集合中的对象仅是受监视应用程序本身的对象。 但是,由于我们的代理主要是与应用程序线程隔离运行的,因此起初这对我来说没有任何意义。

深入研究时,我发现我们对用户对象的分析触发了对象的其他逃逸,但影响很小。 该应用程序已经产生了大量的对象,主要是通过使用NIO和使用派生联接池。 后一框架的共同之处在于它们依赖于许多短期对象的分配。 例如,fork-join任务通常将自身拆分为多个子任务,这些子任务重复此过程,直到每个任务的有效载荷足够小以至于可以直接计算。 每个这样的任务都由一个有状态的对象表示。 活跃的fork联接池每分钟可以产生数百万个此类对象。 但是,由于任务计算速度很快,因此代表对象可以快速进行收集,并因此被年轻的收集器捕获。

那么这些对象是如何突然变成永久性收藏的呢? 这时,我正在制作一个新的缝合工具的原型,以跟踪此类fork联接任务之间的上下文切换。 遵循fork联接任务的路径并非易事。 派生联接池的每个工作线程都会应用工作窃取,并且可能会将任务从其他任何任务的队列中移出。 同样,任务可能在完成时向其父任务提供反馈。 结果,跟踪任务的扩展和交互是一个相当复杂的过程,还因为存在所谓的连续线程,其中单个任务可能会在几毫秒内将作业反弹到数百个线程。 我想出了一个相当优雅的解决方案,该解决方案依赖于许多短期对象的分配,每当将任务回溯到源头时,这些对象就会以突发方式分配。 事实证明,这些爆发本身触发了许多年轻的收藏。

这就是我没有考虑的问题:每个年轻一代的收集都会增加此时不符合垃圾收集条件的任何对象的使用期限。 一个对象不会随时间而老化,而是取决于触发的年轻集合的数量。 并非所有的收集算法都适用,但对于其中的许多算法(例如HotSpot的所有默认收集器 )而言并非如此。 通过触发这么多集合,代理可以线程化受监视应用程序的“过早成熟”的对象,尽管这些对象与代理的对象无关。 以某种方式,运行代理“过早地成熟了”目标应用程序的对象。

解决问题

起初我确实不知道该如何解决。 最后,无法告诉垃圾回收器分别处理“您的对象”。 只要代理线程以比主机进程更快的速度分配寿命较短的对象,它就会破坏原始对象进入租期集合,从而增加垃圾回收暂停。 为了避免这种情况,因此我开始合并我正在使用的对象。 通过池化,我很快将自己的对象成熟到了永久性集合中,垃圾回收行为恢复了其正常状态。 传统上,使用池来避免分配的费用,这在我们这个时代变得很便宜。 我重新发现了它,以消除“外来进程”对垃圾回收的影响,而这仅占用了几千字节的内存。

我们的跟踪器已经在其他地方合并对象。 例如,我们将入口和出口表示为线程局部值,其中包含一堆原始值,这些变量在不分配单个对象的情况下进行了变异。 尽管这种可变的,通常是过程式的和对象池式的编程已不再流行,但事实证明它对性能非常友好。 最后,使位更接近处理器的实际操作。 通过使用固定大小的预分配数组而不是不可变集合,我们为我们节省了很多往返内存的时间,同时还保留了仅包含在少数高速缓存行中的状态。

这是“现实世界”的问题吗?

您可能会认为这是一个相当具体的问题,大多数人无需担心。 但实际上,我描述的问题适用于大量Java应用程序。 例如,在应用程序容器内,我们通常在单个Java进程中部署多个应用程序。 与上述情况一样,垃圾收集算法不会按应用程序对对象进行分组,因为它没有这种部署模型的概念。 因此,共享一个容器的两个隔离应用程序进行的对象分配确实会干扰彼此的预期收集模式。 如果每个应用程序依靠其对象而早逝,则堆的共享会导致短期生存期的强烈相关性。

我不是微服务的拥护者。 实际上,我认为对于大多数应用程序来说,这是一个坏主意。 在我看来,除非有充分的技术理由,否则理想情况下应该将只能存在于交互中的例程一起部署。 即使隔离的应用程序简化了开发,您也可以快速付出运营成本。 我只是提到这一点,以避免对上述经验的道德产生误解。

这种经验告诉我的是,如果一个应用程序是异构的,则在单个Java进程中部署多个应用程序可能不是一个好主意。 例如,在与Web服务器并行运行批处理时,应考虑在各自的进程中运行它们,而不是将它们都部署在同一容器中。 通常,批处理过程以与Web服务器不同的速率分配对象。 但是,许多企业框架仍在宣传解决此类问题的多合一解决方案,这些解决方案不应共享一个开始的过程。 在2016年,附加进程的开销通常不是问题,并且由于内存便宜,因此请升级服务器而不是共享堆。 否则,您可能最终会获得孤立开发,运行和测试应用程序时无法预期的收集模式。

翻译自: https://www.javacodegeeks.com/2016/10/generational-disparity-garbage-collection.html

垃圾收集中的代际差异相关推荐

  1. FPGA逻辑设计回顾(9)DDR的前世今生以及演变过程中的技术差异

    文章目录 前言 DDR的前世SDRAM DDR的今生以及演变版本:DDR/DDR2/DDR3 DDR/DDR2/DDR3/DDR4之间简单对比 速度对比 电压对比 延迟对比 预取差异 电阻端接对比 物 ...

  2. 013 Dealing with Label Quality Disparity in Federated Learning(联邦学习中标签质量差异的处理)

    方法:FOC 目的:解决联邦学习中标签质量差异的问题 结论:优于FedAvg 能有效识别带有噪声标签的参与者,降低噪声标签对联邦学习性能的影响 谷仓效应,亦称筒仓效应,指企业内部因缺少沟通,部门间各自 ...

  3. 火狐浏览器设置url编码_浅谈不同浏览器地址栏中编码的差异

    今天是上交学院专业 ,权当是娱乐而已,拿不拿奖就是另外一回事了.貌似这篇论文跟我的专业没什么必然的联系,倒是他们网工专业的刚好适合,但不知为什么写这类型的我就特别顺手... 摘要: 本文介绍了中文版本 ...

  4. 基于图和基于对齐的混合纠错方法在易错长读中的性能差异

    基于图和基于对齐的混合纠错方法在易错长读中的性能差异 王安琪& 金辉区 基因组生物学 卷 21,产品编号:  14(2020) 引用本文 2079次访问 1引文 18高度 指标细节 抽象 容易 ...

  5. 跨文化合作:如何解决海外网红营销中的文化差异?

    随着社交媒体的快速发展,海外网红营销已成为许多品牌和企业获取国际市场的有效方式.然而,由于不同国家和地区存在着独特的文化差异,如语言.价值观.习俗等,这也给品牌进行海外网红营销带来了一系列挑战.本文N ...

  6. 深度解析dba_segments和sys.seg$中的细节差异(下)

    继续昨天的内容 http://blog.itpub.net/23718752/viewspace-1624762/ 我们已经根据dba_segments和sys.seg$的不同发现最后的差距有2T左右 ...

  7. 两组回归系数差异检验_调节效应检验中的回归系数差异检验

    在调节变量为二分类变量时调节效应检验中,需要对不同取值的调节变量的情况下自变量对因变量的影响进行检验,以确定是否有调节效应,这时候就需要对回归系数差异进行检验.具体检验方法如下: 使用公式辅以SPSS ...

  8. MOS管在缓启电路中的应用差异

    MOS管应用在缓启中电路差异带来的影响 缓启的作用 为什么要延时输出 为什么要改变电流上升斜率 简单的MOS缓启电路 只有QQ1的时候 只有QQ2的时候 QQ1和QQ2同时存在时 总结 缓启的作用 一 ...

  9. 使用 MEME 分析不同类型的 NB-ARC 结构域中 Motif 的差异

    NBS-LRR 基因家族是一种常见的抗病基因家族,该基因家族在所有抗病基因中占比超过 60%.一般被子植物的基因组中包含数百个 NBS-LRR 基因家族的基因. NBS-LRR 基因家族编码的蛋白可以 ...

最新文章

  1. node.js与python
  2. 用PAM实现用户和主机的Samba访问控制
  3. Mac安装mysqldb
  4. 从0到1:构建强大且易用的规则引擎
  5. 模糊神经网络PID控制的一个例子
  6. 苹果电脑怎么删除软件_误格式化,删除文件怎么恢复?3款最好用的数据恢复软件推荐...
  7. 【HDU - 5706】GirlCat(bfs)
  8. 038、JVM实战总结:200小时积累,6小时烹制,史上最强图,图解:大厂面试题,Young GC和Full GC分别在什么情况下会发生?
  9. Ansible自动化运维
  10. Python编程输出所有的“水仙花数”
  11. 渥太华大学计算机科学,【加拿大渥太华大学计算机科学排名第四】渥太华大学录取条件...
  12. 【WebGoat习题解析】Parameter Tampering-Bypass HTML Field Restrictions
  13. python中如何判断词性_Python3自然语言处理——词性标注
  14. 社科研究中的问卷设计详解——结合论文具体例子来看
  15. Jaca 有n个整数,使其前面各数顺序向后移m个位置,最后m个数变成最前面的m个数
  16. 当代最值得收藏的画家作品_当代最具收藏价值的画家:许敬如作品欣赏
  17. python口算训练出题
  18. 网络工程EWM筛选试题
  19. 从网页到微信小程序开发:一:小程序与普通网页的区别
  20. Patchwork++:基于点云的快速、稳健的地面分割方法

热门文章

  1. Java命令学习系列(三)——Jmap
  2. Java 8 HashMap键与Comparable接口
  3. 漫画:什么是动态规划?(整合版)
  4. 虚拟机和linux的安装
  5. 《呐喊》金句摘抄(一)
  6. fastdfs 集群 java_FastDFS集群部署(转载 写的比较好)
  7. java编译器API——使用编译工具
  8. leetcode初级算法4.只出现一次的数字
  9. kafka 发布-订阅模式_使用Apache Kafka作为消息系统的发布-订阅通信中的微服务,并通过集成测试进行了验证...
  10. openjdk和jdk_JDK 11:发行候选更新和OpenJDK JDK 11 LTS