`

ejb与java序列化(1)--发现并分析问题

    博客分类:
  • ejb
阅读更多

这是加入新公司后接手的第一个项目,使用weblogic9.2 + ejb2.0,压力测试时发现速度非常慢,响应时间很不理想,检查日志发现,某些ejb相互调用时方法调用的时间非常长,高达300-500毫秒。非常夸张,因为两个日志之间只是间隔了一个ejb调用。通过thread dump分析后发现有相当多的线程在wait,检查线程调用绽发现是在将参数进行序列化时,线程试图加锁但是锁被占用,因此处于等待状态。考虑到thread dump的这一瞬间,有多达30-50个线程都在同时试图在同一个锁上加锁,很明显这里的锁竞争非常严重。

        因此强烈怀疑是java的序列化机制导致的问题,由于weblogic/ejb之类的太复杂不方便测试,因此单独写了一个类来模拟这种场景:
1. 有大量线程在运行,期间都有序列化的操作
2. 被序列化的对象比较大,有大量子对象(子对象的子对象...)

        运行测试代码,问题重现,测试用开了50个线程,thread dump并分析thread dump信息,发现49个线程处于waiting 状态,只有一个线程在干活!因此抛开weblogic/ejb单独分析java的序列化机制,首先看thread dump的线程信息:

占有锁的线程:
"testthread21" prio=6 tid=0x0ad2d3b8 nid=0x224 runnable [0x0b48f000..0x0b48fce4]
    at java.io.ObjectStreamClass.processQueue(Unknown Source)
    at java.io.ObjectStreamClass.lookup(Unknown Source)
    at java.io.ObjectOutputStream.writeObject0(Unknown Source)
    at java.io.ObjectOutputStream.writeObject(Unknown Source)
    at java.util.ArrayList.writeObject(Unknown Source)
    at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at java.io.ObjectStreamClass.invokeWriteObject(Unknown Source)
    at java.io.ObjectOutputStream.writeSerialData(Unknown Source)
    at java.io.ObjectOutputStream.writeOrdinaryObject(Unknown Source)
    at java.io.ObjectOutputStream.writeObject0(Unknown Source)
    at java.io.ObjectOutputStream.writeObject(Unknown Source)
    at test.Test.test(Test.java:68)
    at test.Test.run(Test.java:53)
    at java.lang.Thread.run(Unknown Source)

等待加锁的线程之一:
"testthread49" prio=6 tid=0x0ad9a508 nid=0xa9c waiting for monitor entry [0x0bb8f000..0x0bb8fbe4]
    at java.lang.ref.ReferenceQueue.poll(Unknown Source)
    - waiting to lock <0x02fb1d40> (a java.lang.ref.ReferenceQueue$Lock)
    at java.io.ObjectStreamClass.processQueue(Unknown Source)
    at java.io.ObjectStreamClass.lookup(Unknown Source)
    at java.io.ObjectOutputStream.writeObject0(Unknown Source)
    at java.io.ObjectOutputStream.writeObject(Unknown Source)
    at java.util.ArrayList.writeObject(Unknown Source)
    at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at java.io.ObjectStreamClass.invokeWriteObject(Unknown Source)
    at java.io.ObjectOutputStream.writeSerialData(Unknown Source)
    at java.io.ObjectOutputStream.writeOrdinaryObject(Unknown Source)
    at java.io.ObjectOutputStream.writeObject0(Unknown Source)
    at java.io.ObjectOutputStream.writeObject(Unknown Source)
    at test.Test.test(Test.java:68)
    at test.Test.run(Test.java:53)
    at java.lang.Thread.run(Unknown Source)

可以发现问题发生在类java.io.ObjectStreamClass中的方法processQueue()方法中:
    static void processQueue(ReferenceQueue<Class<?>> queue,
                    ConcurrentMap<? extends
                 WeakReference<Class<?>>, ?> map)
    {
    Reference<? extends Class<?>> ref;
    while((ref = queue.poll()) != null) {
        map.remove(ref);
    }   
    }

这里的queue.poll()有加锁,继续跟进poll()的代码:
    public Reference<? extends T> poll() {
    if(queueEmpty) return null;
    synchronized (lock) {
        return reallyPoll();
    }
    }
可以看到通过synchronized 关键字,在lock对象有加锁操作,和thread dump信息相符,看来问题就在这里了。

开始分析了,首先看现象,“50个线程1个进入synchronized 块获得锁,49个在试图加锁而处于waiting状态”,首先想到的是,synchronized块中的代码执行的时间比较长,造成锁长时间处于被占有状态,其他线程只能等。但是查看reallyPoll()方法发现它的实现非常简单,执行应该非常快,后面的profile信息也证实了这点。

profile统计信息:
method            num calls    cumulative time        mothod time    percentage1    percentage2

thread=1
invokeWriteObject         387         30209                       2       
lookup                      774778         17499                 6605            57.9%
processQueue          774778           5523                  2185       
poll                          774786           3338                  2294                                    7.59%
reallyPoll                  774793           1045                  1045       

thread=2
invokeWriteObject         739         59715                       3
lookup                    1479482         34550               12815         57.85%
processQueue         1479482        11878                  4236
poll                         1479490          7642                  5656                                    9.47%
reallyPoll                 1479497          1987                  1987       

thread=5
invokeWriteObject        721        147203                        3
lookup                   1443446         99665                 24631        67.7%   
processQueue        1443446         58211                   7044
poll                        1443454         51167                 49196                                    51.33%
reallyPoll                1443461           1971                   1971       

thread=50
invokeWriteObject        701        1438319                       3       
lookup                   1403406        1369971             228508        95.24%
processQueue        1403406        1125351                 6801       
poll                        1403414        1118550            1116462                                   77.62%
reallyPoll                1403421              2089                  2089       

注释:
num calls = number of methed calls
percentage1=lookup cumulative time / invokeWriteObject cumulative time
percentage2=poll mothod time / invokeWriteObject cumulative time

注意:当我们序列化一个Data对象的实例时,因为这个实例里面带有一个包含1000个DataItem实例的ArrayList,而每个DataItem实例有一个int和一个String。所以当invokeWriteObject方法被调用一次时,方法lookup/processQueue/poll/reallyPoll将被调用2000次。同样等待锁,加持锁和释放锁的操作也要执行2000次,虽然真正的业务方法reallyPoll执行的非常快。


        可以看到当线程增加时,percentage1、percentage2的变化已经可以说明问题。因此,最后我给出我的见解:
1. java序列化是cpu依赖型
2. synchronized lock严重影响java序列化

分享到:
评论

相关推荐

    java反序列化工具

    Java反序列化是一种将已序列化的对象状态转换回对象的过程,它是Java平台中持久化数据的一种常见方式。在Java应用程序中,序列化用于保存对象的状态以便稍后恢复,或者在网络间传输对象。然而,这个过程也可能引入...

    java序列化全解

    Java序列化是Java平台中的一种核心机制,它允许对象的状态被转换成字节流,以便存储到磁盘、数据库,或者在网络中进行传输。这对于实现持久化、远程方法调用(RMI)以及Enterprise JavaBeans(EJB)等高级功能至关...

    java反序列化利用工具

    1. 分析目标应用程序的序列化流程,找出可利用的类或方法。 2. 构造恶意的序列化对象,该对象在反序列化时能触发特定行为。 3. 将这个对象发送给服务器,通常是通过HTTP请求或其他网络接口。 4. 如果成功,攻击者...

    如何正确的使用Java序列化技术

    此外,还将深入讨论如何精准控制序列化机制,帮助读者在实际编程中正确运用Java序列化技术,避免常见误区,并掌握高效利用该技术的方法。 **关键词:** 序列化(Serialize)、反序列化(DeSerialize)、类加载...

    什么是java序列化,如何实现java序列化?学习.pdf

    Java 序列化机制是一种将对象状态写入 Byte 流里的技术,并可以从其它地方把该 Byte 流里的数据读出来,重新构造一个相同的对象。这种机制允许将对象通过网络进行传播,并可以随时把对象持久化到数据库、文件等系统...

    java源码包---java 源码 大量 实例

     Java生成密钥、保存密钥的实例源码,通过本源码可以了解到Java如何产生单钥加密的密钥(myKey)、产生双钥的密钥对(keyPair)、如何保存公钥的字节数组、保存私钥到文件privateKey.dat、如何用Java对象序列化保存私钥...

    java反射与EJBjava反射与EJBjava反射与EJBjava反射与EJBjava反射与EJB

    反射的应用场景广泛,例如在框架开发中用于动态调用方法,实现插件化,以及在序列化和反序列化过程中发挥作用。 EJB,全称Enterprise JavaBeans,是Java EE平台的一部分,主要用于构建可部署在服务器端的企业级应用...

    介绍Java对象序列化使用基础

    序列化的过程就是对象写入字节流和从字节流中读取对象。将对象状态转换成字节流之后,可以用java.io包中的各种字节流类将其...对象序列化问题在网络编程中并不是最激动人心的课题,但却相当重要,具有许多实用意义。

    java用ejb面试题

    它通常具有无参数构造器,并实现Serializable接口以支持序列化。 - EJB是重量级的组件,设计用于分布式的企业级应用。它可以是会话bean(处理业务逻辑)、实体bean(表示持久数据)或消息驱动bean(处理JMS消息)。...

    Java序列化技术的探讨.pdf

    本文将介绍几种常用的Java序列化技术,并通过比较、分析这些技术在功能、序列化后的字节数和序列化、反序列化速度方面的差异,得出了在什么应用环境下应该如何选择序列化技术框架。 对象序列化概述 对象序列化是一...

    什么是java序列化,如何实现java序列化?.pdf

    Java 序列化机制是 RMI、EJB 等技术的技术基础。用途包括: 1. 利用对象的串行化实现保存应用程序的当前工作状态,下次再启动的时候将自动地恢复到上次执行的状态。 2. 序列化就是一种用来处理对象流的机制,所谓...

    java序列化

    Java序列化是Java平台提供的一种持久化对象状态的机制,它可以将对象转换成字节流,以便存储到磁盘上,或者在网络中进行传输。这一过程被称为对象的序列化。相反,将字节流还原为原来的对象的过程叫做反序列化。 ...

    java笔试题大集合及答案(jsp-java-ejb公司笔试题)

    3. **IO流**:字节流、字符流、缓冲流、对象序列化。 4. **多线程**:线程的创建方式,同步机制(synchronized关键字,wait/notify,Lock接口)。 5. **网络编程**:Socket编程,HTTP协议的理解。 6. **设计模式**:...

    反序列化作用

    在使用Java序列化机制时,有几个注意事项: a) 序列化对象时,需要确保对象类及其成员变量都是可序列化的,否则会抛出NotSerializableException异常。 b) 如果类中包含对象的引用,序列化时会递归地序列化所有引用...

    Java序列化与反序列化三种格式存取

    序列化机制在Java中有着广泛的应用,EJB、RMI等技术都是以此为基础的。  正确使用序列化机制  一般而言,要使得一个类可以序列化,只需简单实现java.io.Serializable接口即可(还要实现无参数的构造方法)。该...

    Java网络程序设计-第7章.ppt

    Java序列化在网络编程(如Socket、RMI、JMS、EJB)中扮演着关键角色。例如,在Socket通信中,序列化使得对象可以通过网络通道安全地传输。RMI允许远程调用方法,序列化确保了对象的状态可以在客户端和服务器之间正确...

    Java对象序列化.pdf

    Java EE服务器在处理EJB调用时,通常需要将对象序列化,以便在不同的Java虚拟机之间传递。HTTP协议也经常利用对象序列化,例如在Web服务中,将对象编码为JSON或XML格式进行通信。JDK提供了一套内置的序列化机制,...

    ejb-3_0-pfd-spec-simplified.pdf

    - **含义**: 该描述提到了与 EJB 3.0 相关的一系列文档,包括简化版、持久化版本和核心版本。作者自称为“菜鸟”,表明其为初学者,并对这些文档之间的具体区别不清楚。 #### 标签解析 - **标签**: "ejb ...

    Core Java(Volume II--Advanced Features 9th Edition).pdf

    6. **Java序列化**:Java序列化用于将对象状态转化为字节流,便于存储和网络传输。书中讨论了如何实现Serializable接口,序列化和反序列化过程,以及自定义序列化行为。 7. **异常处理**:Java的异常处理机制是其...

    Java EJB 经典面试题 面试时被问过几个

    通常,Bean需要有无参构造器以支持容器创建,并实现Serializable接口以支持序列化。而EJB则是Java的分布式组件,基于RMI实现远程方法调用,可以跨进程、跨计算机访问。EJB必须部署在应用服务器中,如Websphere、...

Global site tag (gtag.js) - Google Analytics