Java中第一次尝试序列化的速度慢吗?

Java中第一次尝试序列化的速度慢吗?,java,serialization,io,Java,Serialization,Io,考虑一个简单的程序(发布在下面),它使用“ObjectOutputStream”序列化给定数量的对象。它多次调用同一函数将对象序列化到文件。第一次调用比后续调用花费的时间更长(差异取决于被序列化的对象的数量): 造成这种差异的原因是什么?我尝试在没有序列化的情况下做同样的事情,即编写一个字节数组,但没有这样的区别 更新:如果程序不多次调用同一方法,而是在for循环中序列化对象,然后调用该方法,则会发生同样的情况:后续的方法调用速度更快: "manual" serialization, time

考虑一个简单的程序(发布在下面),它使用“ObjectOutputStream”序列化给定数量的对象。它多次调用同一函数将对象序列化到文件。第一次调用比后续调用花费的时间更长(差异取决于被序列化的对象的数量):

造成这种差异的原因是什么?我尝试在没有序列化的情况下做同样的事情,即编写一个字节数组,但没有这样的区别

更新:如果程序不多次调用同一方法,而是在for循环中序列化对象,然后调用该方法,则会发生同样的情况:后续的方法调用速度更快:

"manual" serialization, time elapsed: 535
Time elapsed: 170ms
Time elapsed: 193ms
Time elapsed: 139ms
因此,JIT编译不会导致这种差异

代码:

import java.io.BufferedOutputStream;
导入java.io.FileOutputStream;
导入java.io.IOException;
导入java.io.ObjectOutputStream;
导入java.io.OutputStream;
导入java.io.Serializable;
导入java.util.ArrayList;
导入java.util.List;
公共类序列化测试{
静态最终整数计数=10000,尝试次数=3;
静态类Simple实现可序列化{
字符串名;
整数指数;
简单(字符串名,整数索引){
this.name=名称;
这个指数=指数;
}
}
公共静态void main(字符串[]args)引发IOException{
int count=计数;
如果(args.length>0){
count=Integer.parseInt(args[0]);
}
列表对象=新的ArrayList();
for(int i=0;i1?args[1]:“对象”;
System.err.println(“序列化”+count+“对象…”);
for(int i=0;i
您在第一次运行它时会产生大量成本,包括JIT编译、类加载、反射等。这是正常的,而且大多数情况下无需担心,因为对生产应用程序的影响可以忽略不计

您在第一次运行时就要付出大量的成本,包括JIT编译、类加载、反射等。这是正常的,大多数情况下不需要担心,因为对生产应用程序的影响可以忽略不计

在Java中,JIT(即时编译器)在经常调用某个方法时进行编译(有些建议调用10.000次)。

但是众所周知,java序列化速度慢,并且占用大量内存。
当您使用DataOutputStream序列化自己时,您可以做得更好。

java内置串行化,如果用于快速演示项目,则可以立即消除bug。

在java中,JIT(即时编译器)在经常调用方法时进行编译(有些建议调用10.000次)。

但是众所周知,java序列化速度慢,并且占用大量内存。
当您使用DataOutputStream序列化自己时,您可以做得更好。


java内置串行化,如果用于快速演示项目,则可以立即消除bug。

JVM为程序中的每个方法维护一个
调用计数。每次在程序中调用相同的方法,其
调用计数都会增加。一旦其
调用计数
达到
JIT编译阈值
,此方法就由
JIT
编译。下一次调用这个方法时,它的执行会更快,因为解释器现在不是解释方法而是执行本机代码。因此,同一方法的第一次调用比随后的调用花费更多的时间。

JVM为程序中的每个方法维护一个
调用计数。每次在程序中调用同一方法,其
调用计数都会增加。一旦其
调用计数
达到
JIT编译阈值
,此方法就由
JIT
编译。下一次调用这个方法时,它的执行会更快,因为解释器现在不是解释方法而是执行本机代码。因此,同一方法的第一次调用比后续调用花费更多的时间。

完整的答案是:

  • ObjectOutputStream对于正在序列化的几种类型的对象具有一些内部静态缓存(请参阅),因此相同类型对象的后续序列化速度比第一种快

  • 如果考虑编译
    ObjectOutputStream.writeObject
    (而不是其他答案中提到的用户定义方法),JIT编译可能会影响性能。感谢所有在回答中提到JIT编译的人


  • 这也解释了为什么在编写字节数组而不是序列化对象时没有区别:a)没有静态缓存和b)
    FileOutputStream.write(byte[])
    调用本机
    writeBytes
    ,几乎不进行JIT编译。

    完整的答案是:

  • 对象输出树
    "manual" serialization, time elapsed: 535
    Time elapsed: 170ms
    Time elapsed: 193ms
    Time elapsed: 139ms
    
    import java.io.BufferedOutputStream;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.ObjectOutputStream;
    import java.io.OutputStream;
    import java.io.Serializable;
    import java.util.ArrayList;
    import java.util.List;
    
    public class SerializationTest {
        static final int COUNT = 10000, TRIES = 3;
    
        static class Simple implements Serializable {
    
            String name;
            int index;
    
            Simple(String name, int index) {
                this.name = name;
                this.index = index;
            }
        }
    
        public static void main(String[] args) throws IOException {
            int count = COUNT;
            if (args.length > 0) {
                count = Integer.parseInt(args[0]);
            }
            List<Simple> objects = new ArrayList<Simple>();
            for (int i = 0; i < count; i++) {
                objects.add(new Simple("simple" + i, i));
            }
            String filename = args.length > 1 ? args[1] : "objects";
    
            System.err.println("Serializing " + count + " objects...");
            for(int i = 0; i < TRIES; i++) {
                System.err.println("Time elapsed: " + 
                                   serializeOneByOne(objects, filename + i + ".bin", false) + "ms");
            }
        }
    
        static long serializeOneByOne(List<?> objects, String filename, boolean buffered)
                                                                        throws IOException {
            OutputStream underlying = new FileOutputStream(filename);
            if (buffered) {
                underlying = new BufferedOutputStream(underlying);
            }
            ObjectOutputStream output = new ObjectOutputStream(underlying);
            // take started after the output stream is open
            // although it does not make a big difference
            long started = System.currentTimeMillis();
    
            try {
                for (Object s : objects) {
                    output.writeObject(s);
                }
            } finally {
                output.close();
            }
            long ended = System.currentTimeMillis();
            return ended - started;
        }
    }