化鲲为鹏,我有话说【coredump问题】华为taishan2280服务器 JNI程序奔溃问题分析报告
化鲲为鹏,我有话说
一、问题描述
在华为taishan 2280裸金属服务器上,安装Neokylin V7Update5版本操作系统。在该操作系统之上,执行业务代码,代码执行一段时间后发生coredump。程序奔溃。
类别 | 子项 | 版本 | 获取地址(方法) |
裸金属 服务器 | Taishan 2280 | 客户 | |
OS | Neokylin | V7Update5 | 客户 |
Kernel | 4.14 | 客户 | |
软件包 | Java-1.8.0-openjdk | 1.8.0 | 客户 |
基础环境
二、定位过程
(一)怀疑点list
怀疑点一、JNI的垃圾处理程序出错,导致内存泄露。
怀疑依据,问题现象是在测试过程中发包间隔越快,100MS,10MS,1MS三种测试场景,内存堵塞越快。发包越快可能导致内存不断分配,如果没有回收机制,就会出现因为内存被分配完导致程序奔溃的现象。
怀疑点二、字符集(错把Modified UTF-8当成UTF-8)的处理错误导致程序奔溃。
怀疑依据,错把Modified UTF-8当成UTF-8,这是一类JNI常见程序奔溃问题。
Java与Jni交互时,在Jni层字符编码为Modified UTF-8。通过jni的NewStringUTF方法把C++的字符串转换为jstring时,如果入参为emoji表情或其他非Modified UTF8编码字符将导致Crash。另外使用jni的GetStringUTFChars方法把jstring转换为C++字符串时得到的字符串编码为Modified UTF8,如果直接传递到服务端或其他使用方,emoji表情将出现解析失败的问题。
Modified UTF-8的特点:
标准和变种的UTF-8有两个不同点。
第一,空字符(null character,U+0000)使用双字节的0xc0 0x80,而不是单字节的0x00。这保证了在已编码字符串中没有嵌入空字节。因为C语言等语言程序中,单字节空字符是用来标志字符串结尾的。当已编码字符串放到这样的语言中处理,一个嵌入的空字符将把字符串一刀两断。
第二,基本多文种平面之外字符的编码的方法。在标准UTF-8中,这些字符使用4字节形式编码,而在改正的UTF-8中,这些字符和UTF-16一样首先表示为代理对(surrogate pairs),然后再像CESU-8那样按照代理对分别编码。这样改正的原因更是微妙。Java中的字符为16位长,因此一些Unicode字符需要两个Java字符来表示。语言的这个性质盖过了Unicode的增补平面的要求。尽管如此,为了要保持良好的向后兼容、要改变也不容易了。这个改正的编码系统保证了一个已编码字符串可以一次编为一个UTF-16码,而不是一次一个Unicode码点。不幸的是,这也意味着UTF-8中需要4字节的字符在变种UTF-8中变成需要6字节。(摘自维基百科)
因此在与其他组件进行交互或与服务端进行通信时要注意不要误把变种Modified UTF-8当成UTF-8数据。可以先将Java的String用UTF-8编码转换成byte数组,再转换成C/C++字符串即可保证字符编码为UTF-8。
标准写法:
jclass str_class = GetClass("java/lang/String");
jmethodID init_mid = JNIENV->GetMethodID(str_class, "<init>", "([BLjava/lang/String;)V");
jbyteArray bytes = JNIENV->NewByteArray(size);
JNIENV->SetByteArrayRegion(bytes, 0, size, (jbyte *)buffer);
jstring encoding = JNIENV->NewStringUTF("utf-8");
jstring result = (jstring)JNIENV->NewObject(str_class, init_mid, bytes, encoding);
JNIENV->DeleteLocalRef(str_class);
走读业务代码,怀疑是少了JNIENV->NewStringUTF("utf-8")字符集的转换。
怀疑点三、openJDK程序相比JDK程序少了部分功能,需要使用JDK代替oepnJDK。
怀疑依据,openjdk是jdk的开放原始码版本,以GPL协议的形式放出。大部分原始码都相同,只有少部分原始码被替换掉。
关于JDK和OpenJDK的区别,可以归纳为以下几点:
授权协议的不同:
openjdk采用GPL V2协议放出,而JDK则采用JRL放出。两者协议虽然都是开放源代码的,但是在使用上的不同在于GPL V2允许在商业上使用,而JRL只允许个人研究使用。
OpenJDK不包含Deployment(部署)功能:
部署的功能包括:Browser Plugin、Java Web Start、以及Java控制面板,这些功能在Openjdk中是找不到的。
OpenJDK源代码不完整:
在采用GPL协议的Openjdk中,sun jdk的一部分源代码因为产权的问题无法开放openjdk使用,其中最主要的部份就是JMX中的可选元件SNMP部份的代码。因此这些不能开放的源代码 将它作成plug,以供OpenJDK编译时使用,你也可以选择不要使用plug。而Icedtea则为这些不完整的部分开发了相同功能的源代码 (OpenJDK6),促使OpenJDK更加完整。
部分源代码用开源代码替换:
由于产权的问题,很多产权不是SUN的源代码被替换成一些功能相同的开源代码,比如说字体栅格化引擎,使用Free Type代替。
openjdk只包含最精简的JDK:
OpenJDK不包含其他的软件包,比如Rhino Java DB JAXP……,并且可以分离的软件包也都是尽量的分离,但是这大多数都是自由软件,你可以自己下载加入。
不能使用Java商标:
在安装openjdk的机器上,输入“java -version”显示的是openjdk,但是如果是使用Icedtea补丁的openjdk,显示的是java。
根据上面的信息知道,openJDK是JDK的精简版,JDK的部分功能openJDK没有。那就是说openJDK和JDK只是功能范围不一致,不应该出现程序奔溃的bug。所以这个怀疑点可能性非常低。可以放在最后去排查。
怀疑点四、jchar类型在编译阶段,在x86和arm64架构的服务器上默认行为不一致。
怀疑依据,经验,之前处理过因char数据类型在x86和arm64架构的服务器上编译时默认行为不一致导致的程序奔溃。
(二)怀疑点验证步骤
1、通过与技术员工沟通,发生程序奔溃时生成的core文件中是数组下标溢出,并非内存耗尽(OOM)。所以排除怀疑点一。
2、经过进一步分析业务代码,发现接收包的函数是单字节数组,一个个字符进行拷贝,不存在字符集的转换。所以排除怀疑点二。
3、当问题复现后(第一次问题出现时的现象被破坏,core文件及日志被删除),查看core文件(core文件分析方法使用gdb工具,该方法有专门的一个小册子说明,此处不详细讲解),发现调用栈报错在libjvm.so库文件处,查看libjvm.so库文件所属的rpm包(rpm -qf /xxx/libjvm.so 即libjvm.so文件的绝对路径),发现libjvm.so库文件不属于任何rpm包。咨询技术人员,该libjvm.so库文件是从一个叫jdk的压缩包解压的,至于这个jdk包的来源已经不可追溯了。到这里,立马怀疑是libjvm.so库文件与当前服务器的操作系统不匹配。
下面是验证过程。
将neokylin arm64版本的iso上传至服务器,并挂载、配置为yum源。
mount nsV7Update5-adv-lic-build06-aarch64.iso /mnt
touch /etc/yum.repos.d/test-iso.repo
把下面一段灰色的内容拷贝到test-iso.repo文件,保存并退出。
[test]
name=test-iso
baseurl=file:///mnt
gpgcheck=0
gpgkey=
使用yum安装java-1.8.0-openjdk程序(yum install java-1.8.0-openjdk),之后请技术人员执行他们的业务程序,分别就发包间隔100MS,10MS,1MS三种场景做了测试,程序正常运行,不再发生程序奔溃。到此,怀疑点三得到证实。
至此,问题解决。就是java-1.8.0-openjdk程序与运行环境的操作系统不匹配导致的。
三、问题根因
操作系统上安装的java-1.8.0-openjdk程序与Neokylin V7Update5操作系统版本不匹配(java-1.8.0-openjdk程序包来源不可追溯)。
四、解决方案
安装Neokylin V7Update5版本安装包自带的java-1.8.0-openjdk程序即可。
五、验证结果
执行业务代码,尝试100MS,10MS,1MS三种发包间隔测试场景,均不再出现coredump问题,程序正常运行。问题解决。
六、思考&建议
1、当发现或者怀疑系统中的程序与操作系统可能不匹配时,首先确认程序包的来源,是否从官网获取,是否与当前环境操作系统匹配。程序安装包务必从官网获取,务必与系统配套。
2、问题现场不要破坏,如果发生coredump,一定要保存好core文件及当时的日志,方便准确定位。因为描述的现象和真实的场景存在差异。
- 点赞
- 收藏
- 关注作者
评论(0)