一个fastjson转换JSON字符串的报错排查

今天给一个java类加了几个字段,没想到转为json字符串的时候报错了,定位了一下原因,觉得这种情况遇到的应该不多,又想起来很久没写博客了,于是就把遇到的问题以及定位过程记下来,省了以后遇到的人再花时间定位了

调用的是JSON.toJSONString(Object object)方法,具体的报错信息如下:

Caused by: com.alibaba.fastjson.JSONException: write javaBean error
    at com.alibaba.fastjson.serializer.JavaBeanSerializer.write(JavaBeanSerializer.java:234)
    at Serializer_5.write1(Unknown Source)
    at Serializer_5.write(Unknown Source)
    at com.alibaba.fastjson.serializer.JSONSerializer.write(JSONSerializer.java:369)
    at com.alibaba.fastjson.JSON.toJSONString(JSON.java:393)
    at com.alibaba.fastjson.JSON.toJSONString(JSON.java:567)
    at net.sf.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
    at org.springframework.aop.framework.Cglib2AopProxy$CglibMethodInvocation.invokeJoinpoint(Cglib2AopProxy.java:689)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
    at org.springframework.aop.aspectj.AspectJAfterThrowingAdvice.invoke(AspectJAfterThrowingAdvice.java:55)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
    at 
    ... 81 common frames omitted
Caused by: com.alibaba.fastjson.JSONException: create asm serializer error, class class com.feifei.bean.TestBean
    at com.alibaba.fastjson.serializer.SerializeConfig.createJavaBeanSerializer(SerializeConfig.java:113)
    at com.alibaba.fastjson.serializer.JSONSerializer.getObjectWriter(JSONSerializer.java:527)
    at com.alibaba.fastjson.serializer.ListSerializer.write(ListSerializer.java:81)
    at com.alibaba.fastjson.serializer.ObjectFieldSerializer.writeValue(ObjectFieldSerializer.java:118)
    at com.alibaba.fastjson.serializer.ObjectFieldSerializer.writeProperty(ObjectFieldSerializer.java:67)
    at com.alibaba.fastjson.serializer.JavaBeanSerializer.write(JavaBeanSerializer.java:216)
    ... 94 common frames omitted
Caused by: java.lang.ClassFormatError: JVMCFRE042 bytecode array size > 65535; class=Serializer_57, offset=53261
    at java.lang.ClassLoader.defineClassImpl(Native Method)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:275)
    at com.alibaba.fastjson.util.ASMClassLoader.defineClassPublic(ASMClassLoader.java:42)
    at com.alibaba.fastjson.serializer.ASMSerializerFactory.createJavaBeanSerializer(ASMSerializerFactory.java:287)
    at com.alibaba.fastjson.serializer.ASMSerializerFactory.createJavaBeanSerializer(ASMSerializerFactory.java:36)
    at com.alibaba.fastjson.serializer.SerializeConfig.createASMSerializer(SerializeConfig.java:78)
    at com.alibaba.fastjson.serializer.SerializeConfig.createJavaBeanSerializer(SerializeConfig.java:106)
    ... 99 common frames omitted

可以看到,代码在执行到ASMClassLoader.defineClassPublic这个方法时报错了,报的错是:

JVMCFRE042 bytecode array size > 65535

字节数组超长,这个长度65535,很显然是长度规定了2的16次方(65536),也就是64K大小。

再来看看ASMClassLoader这个类,这个类继承了java的类装载器ClassLoader,fastjson内嵌了ASM框架来动态生成类;

public class ASMClassLoader extends ClassLoader {

并且报错的方法其实就是调用了ClassLoader的类定义方法defineClass,本质上调用的是JVM的native功能

public Class<?> defineClassPublic(String name, byte[] b, int off, int len) throws ClassFormatError {
        Class<?> clazz = defineClass(name, b, off, len, DOMAIN);

    return clazz;
}

这里其实也就是传的需要生成的类的字节码,在这里也就对应着我们需要转换成JSON串的对象类的字节码,所以这个报错应该就是对象类字节码超长了,也就是类里面的东西太多了,试着删掉这个bean里的一些变量,测试一把,果然不报错了。

那到底是什么太长了呢?这也好办,继续写个单元测试,把ASMSerializerFactory类拷出来,把字节码输出看看

单元测试:

@Test
public void test(){
    try{
        this.asmSerial(TestBean.class);
    }catch(Exception e){
        System.out.println(e);
    }
}

private void asmSerial(Class<?> clazz) throws Exception{
    ASMSerializerFactory fac = new ASMSerializerFactory();
    fac.createJavaBeanSerializer(clazz);
}

在拷出来的ASMSerializerFactory类的createJavaBeanSerializer方法中新增输出逻辑:

byte[] code = cw.toByteArray();
// 输出class文件
File outputFile = new File("C:\\Users\\feifei\\Desktop\\ooo\\out.class");  
FileOutputStream outputFileStream = null;  
try {  
    outputFileStream = new FileOutputStream(outputFile);  
} catch (FileNotFoundException e) {  
}  
outputFileStream.write(code);
System.out.println("code:length: " + code.length);

执行一把,果然报错了,而且class文件也成功生成了,先看看控制台输出:

[TestNG] Running:
  C:\Users\feifei\AppData\Local\Temp\testng-eclipse-1963807168\testng-customsuite.xml

code:length: 191423
FAILED: test
java.lang.ClassFormatError: Invalid method Code length 65904 in class file Serializer_1
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClass(Unknown Source)
    at com.alibaba.fastjson.util.ASMClassLoader.defineClassPublic(ASMClassLoader.java:42)
    at ASMSerializerFactory.createJavaBeanSerializer(ASMSerializerFactory.java:299)
    at ASMSerializerFactory.createJavaBeanSerializer(ASMSerializerFactory.java:42)
    at tt.asmSerial(tt.java:19)
    at tt.test(tt.java:11)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)

可见报错更加细化了

java.lang.ClassFormatError: Invalid method Code length 65904 in class file Serializer_1

这个报错意思更加明显了,方法的长度不合法,这个方法的长度是65904,结合上面报出来的大于65535,那么问题就很明显了,java里规定了类的一个方法最大不能超过64K
我们继续打开刚刚生成的class文件,可以看到,我们bean里面的字段都在里面,而里面最大的方法是write和write1,分别占13000多行,所以应该就是这两方法超长了。
writeMeth.png
至此,问题就定位清晰了,就是bean里面字段太多,导致序列化后,生成class文件的方法超长,也就导致转为json字符串失败了,这个除了拆分bean,貌似也没什么好办法了,而且这个bean确实太大了,拆开可读性也会好很多

多说挂了,换回typecho,随便说说

之前从typecho换到了hexo,用了多说的评论。现在多说要挂了,不想用Disqs,网易云跟帖之类的插件了,于是换回typecho.
尴尬地发现之前博客的备份直接用就行了……也就是说这一年多竟然没写一篇博客……我自己也是无语了~
除了懒,有点忙之外,主要精力还是放在主业java上了,毕竟是吃饭的家伙;而且随着一门语言逐渐深入进去,越发觉得自己懂的太少,反而不知道博客能写点什么了
不过折腾还是继续,现在抛开ionic,继续折腾ReactNative,多学点东西,拓展一下眼界还是很有必要的~
生命不息,折腾不止~

Ubuntu 14.04安装java+nginx+tomcat7简要步骤

之前一直用centOS,试用了一次Ubuntu感觉这是一个对新手很友好的系统,用apt-get安装环境很方便,前端时间换了VPS,重新安装环境很快就搞定了,做个笔记,下次再配置就不用再找了

  1. nignx安装

Ubuntu14.04默认安装是是1.4.6版本的,而新的稳定版本都到1.8了,所以我要装个更加新的稳定版本,参考nginx的官方教程http://nginx.org/en/linux_packages.html#stable

先下载一个key,http://nginx.org/keys/nginx_signing.key
添加到apt里

apt-key add nginx_signing.key

再修改/etc/apt/sources.list文件,
新增配置:

deb http://nginx.org/packages/mainline/ubuntu/ trusty nginx
deb-src http://nginx.org/packages/mainline/ubuntu/ trusty nginx

保存之后,执行

apt-get update
apt-get install nginx

这样nginx就一路安装完毕了

安装完的nginx配置在/etc/nginx/conf.d/目录

  1. jdk安装

阅读剩余部分 -

ionic开发的几个小坑

之前做个小移动客户端,图方便快捷,使用了ionic来开发,整体还是挺顺畅的,就是不太习惯yong其中碰到一些小坑,
都是一些知道了就很简单,不知道就很蛋疼的东西,所以做个笔记,帮初次接触ionic和angularjs的人省点事儿
1.ionic中的CORS(跨域)问题
这个问题一般刚接触ionic的都会遇到,在浏览器中通过ionic serve调试的时候,请求api时会报错

XMLHttpRequest cannot load http://localhost:8080/api/p/1. No
'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'http://localhost:8100' is therefore not allowed
access.

阅读剩余部分 -

杂记

唔,发现一晃又是好几个月没写博客了……
神说,这里太荒芜了,于是就有了这篇杂记。
这段时间比较忙(懒),手上压了一些东西要做,当然拉,都是自己折腾着玩的,所以基本三天打鱼两天晒网。继而导致手上压的东西越来越多,自己都有点不记得了……干脆写个博客絮叨絮叨,一举两得~

上个月把服务器从美国换到了日本,在Linode上连试了7,8个IP,总算找到一个能用的日本节点,顺便把系统从centOS换到了Ubuntu。话说之前用centOS觉得挺方便的,一直听说Ubuntu操作简单,于是就趁这个机会换着试试,觉得果然没有最方便,只有更方便,装环境比之前centOS更简单了。
切到日本节点跑了两星期之后发现挺稳定,ping值更是降了一个数量级,于是果断把原先的美国节点删掉了(都是钱呐~~)

阅读剩余部分 -

热评文章

最新文章

最近回复

归档

其它