分类 Java 相关 下的文章

使用 tcpkill 杀掉 tcp 连接

debian/Ubuntu 系列安装 tcpkill

sudo apt install dsniff -y

redhat/CentOS 系列安装 tcpkill

yum -y install dsniff -y

杀掉连接

sudo tcpkill -i eth0 -9 port 50185
sudo tcpkill ip host 10.168.1.1
sudo tcpkill -9 port 32145

查看连接

ss -t

由于频繁 JAXBContext.newInstance 导致应用程序变慢

最近有个应用开始接受一种新的service请求, 请求是由一个 batch 应用触发, 在短时间内产生大量请求, 导致整个应用多个方面出现问题, 比如平均延迟增加, CPU 几乎 100%, GC overhead 在 60% 上下, tomcat 允许的服务线程数达到上限, error 大量增多, timeout 增多.

从已有的数据看, GC overhead 占用 60% 左右, 说明还有 40% 是其它代码占用的. 并且 GC overhead 最终是由引用代码引起的, 从 GC 日志看, heap 总是可以回收, 只是回收慢一些, 说明如果修改了代码的问题, GC 问题大概率会自动修复. 一般由于 transaction time 增加, 会导致停留在内存的短暂数据增加, 导致 GC overhead 升高. 此时, CMS, G1 等 GC 算法之前积累的经验(何时开始回收的时间点) 不适用新的情况.

通过 async-profiler 做出 CPU 的使用火焰图, 发现占用 CPU 40% 的代码绝大部分都是在处理 javax/xml/bind/JAXBContext.newInstance 的事情. 如下:
JAXBContextNewInstance.png

关于为什么 JAXBContext.newInstance 会导致 CPU 比较高的问题, 其实互联网上已经有很多这样的问题: https://www.ibm.com/support/pages/jaxbcontext-initialization-takes-long-time

引用这篇文章里面的:

JAXB context (javax.xml.bind.JAXBContext) object instantiation is a resource intensive operation. JAXB Context instantiation involves the pre-load and pre-creation of contexts (called the pre-caching process) of all packages and classes associated with the context, and then all of the packages and classes which are statically (directly and indirectly) referenced from those. Performance latency will correlate with the number of classes which are passed during JAXB creation during this pre-caching process.

一般对于这种 xxxContext 对象, 都是某个过程, 某个环境只有一个的对象, 对于这个问题当中, 每个请求都尝试创建一个 Request level 的 JAXBContext, 导致应用出现问题.

诊断 mimepull.jar 导致的大量临时文件不能释放的问题

某天某个应用程序的某些服务器突然大量抛出下面异常:

msg=Error executing HystrixCommand 'xxxClient', failureType = COMMAND_EXCEPTION, rootcause=SocketException: Too many open files&st=com.sun.jersey.api.client.ClientHandlerException: java.net.SocketException: Too many open files

很明显, 这个应用打开了太多的文件句柄, 没有关闭. 为了缓解问题, 重启服务器临时解决. 到底哪里出了问题呢? 为什么最近才开始有这种错误? 我们一步步找出事故的原因.

- 阅读剩余部分 -

JDK attach API

从 JDK 1.6 开始, Sun 引入了 attach API, 可以 attach 到目标 Java 进程, 这样就可以和目标 JVM 通信, 建立通信之后, 你可以让目标进程加载某些 agent 代码, 这样你就可以和目标进程进行信息交换了.

最简单的代码就在 API 文档里: https://docs.oracle.com/javase/8/docs/jdk/api/attach/spec/index.html
要使用 attach API, 需要引入 jdk lib 目录下的 tools.jar, 因为里面包含包: com.sun.tools.attach.*.

一旦获得 VirtualMachine, 最简单可以获得目标进程的系统属性. 其他的都是通过让目标进程加载 agent 来获得. agent 分为两类: 一类是通过 java.lang.instrument API 写的 Java agent. 另外一类是通过 Native 代码 JVM TI (The agent must be written in native code) 写的 agent.

有人封装的 attach API: https://github.com/gridkit/jvm-attach
比如有人做的工具: https://github.com/aragozin/jvm-tools

如何写 Java Agent : https://docs.oracle.com/javase/8/docs/api/index.html?java/lang/instrument/package-summary.html
如何写基于 JVMTI 的 agent: https://www.oracle.com/technical-resources/articles/javase/jvmti.html

我写的使用 Attach API 和 Instrument API 的agent: https://github.com/manecocomph/myJavaAgent

其它相关概念:
JPDA (Java™ Platform Debugger Architecture), JVM TI, JDWP(Java Debug Wire Protocol), JDI(Java Debug Interface), Java Agent, HPROF agent, HPROF format, BCI (Byte Code Injection), Instrument API, Java Attach API, AsyncGetCallTrace.

JDI API: https://docs.oracle.com/javase/6/docs/jdk/api/jpda/jdi/index.html
使用 JDI 获得一个类的所有实例对象: https://docs.oracle.com/javase/6/docs/jdk/api/jpda/jdi/com/sun/jdi/ReferenceType.html#instances(long)