Java文件IO因大量连续写入而变慢

时间:2015-06-25 23:55:19

标签: java performance io disk

我写了一个驱动程序擦除程序,旨在安全地覆盖驱动器的可用空间。一开始一切都很好,然后随着时间的推移,速度急剧下降。我有一个1TB驱动器,开始时大约120MB / s然后缓慢下降到70.起初我以为是驱动器,所以我在我的RAID0 velociraptor驱动器上测试了它,它几乎半分钟就达到了160MB / s,然后慢慢地这似乎不仅仅是缓存填满,因为完全放慢需要一分钟左右。

首先,问题在于我是否可能将数据写入磁盘,或者其他语言的硬盘实际上是否正常?

其次,我会看到切换到与速度相关的NIO有什么潜在的好处吗?使用ISAAC擦除是多线程的,因此瓶颈实际上只是它的写入速度。

最后,也许可能是我的速度计算。但是我离开它非常简单,所以我不明白这是怎么回事。

编辑:(一些信息)

  

两者都是常规磁力驱动器。 1TB驱动器是WD 7200rpm。该   raid0设置是两个WD 10,000rpm。运行Windows 7 Ultimate。

     

java版“1.8.0_45”。 Java(TM)SE运行时环境(构建   1.8.0_45-B15)。 Java HotSpot(TM)64位服务器VM(版本25.45-b02,混合模式)。

您可以测试此运行示例:(cleaner.DriveCleaner)

package cleaner;

import java.awt.EventQueue;
import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.security.SecureRandom;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JProgressBar;
import javax.swing.text.NumberFormatter;

/**
 * @author Colby
 */
public class DriveCleaner extends javax.swing.JFrame {

    public DriveCleaner() {
        initComponents();
        refreshDrives();
    }

    protected static boolean running = false;
    private Thread worker;

    private void refreshDrives() {
        File[] roots = File.listRoots();
        drives.setModel(new DefaultComboBoxModel(roots));
    }

    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">                          
    private void initComponents() {

        buttonGroup1 = new javax.swing.ButtonGroup();
        jLabel1 = new javax.swing.JLabel();
        jSeparator1 = new javax.swing.JSeparator();
        drives = new javax.swing.JComboBox();
        normSelect = new javax.swing.JRadioButton();
        randSelect = new javax.swing.JRadioButton();
        jLabel2 = new javax.swing.JLabel();
        jLabel3 = new javax.swing.JLabel();
        passes = new javax.swing.JComboBox();
        jLabel4 = new javax.swing.JLabel();
        runButton = new javax.swing.JButton();
        jSeparator2 = new javax.swing.JSeparator();
        jSeparator3 = new javax.swing.JSeparator();
        progress = new javax.swing.JProgressBar();
        jMenuBar1 = new javax.swing.JMenuBar();
        jMenu1 = new javax.swing.JMenu();
        jMenuItem1 = new javax.swing.JMenuItem();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        setTitle("DriveCleaner V1.0");

        jLabel1.setFont(new java.awt.Font("Consolas", 2, 17)); // NOI18N
        jLabel1.setText("DriveCleaner");

        buttonGroup1.add(normSelect);
        normSelect.setText("Simple Clean");
        normSelect.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                normSelectActionPerformed(evt);
            }
        });

        buttonGroup1.add(randSelect);
        randSelect.setSelected(true);
        randSelect.setText("ISAAC 256 Clean");
        randSelect.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                randSelectActionPerformed(evt);
            }
        });

        jLabel2.setText("Drive:");

        jLabel3.setText("Passes:");

        passes.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "1", "2", "4", "8", "16", "32", "64", "128" }));
        passes.setSelectedIndex(2);

        jLabel4.setText("Method:");

        runButton.setText("Clean");
        runButton.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                runButtonActionPerformed(evt);
            }
        });

        jSeparator2.setOrientation(javax.swing.SwingConstants.VERTICAL);

        progress.setString("");
        progress.setStringPainted(true);

        jMenu1.setText("File");

        jMenuItem1.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_F5, 0));
        jMenuItem1.setText("Refresh Drives");
        jMenuItem1.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jMenuItem1ActionPerformed(evt);
            }
        });
        jMenu1.add(jMenuItem1);

        jMenuBar1.add(jMenu1);

        setJMenuBar(jMenuBar1);

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addComponent(jSeparator1, javax.swing.GroupLayout.Alignment.TRAILING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(jSeparator3)
                    .addGroup(layout.createSequentialGroup()
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                            .addComponent(jLabel1)
                            .addGroup(layout.createSequentialGroup()
                                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                                    .addComponent(drives, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                                    .addComponent(jLabel2))
                                .addGap(18, 18, 18)
                                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                                    .addComponent(passes, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                                    .addComponent(jLabel3))
                                .addGap(18, 18, 18)
                                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                                    .addGroup(layout.createSequentialGroup()
                                        .addComponent(randSelect)
                                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                                        .addComponent(normSelect))
                                    .addComponent(jLabel4))
                                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 31, Short.MAX_VALUE)
                                .addComponent(jSeparator2, javax.swing.GroupLayout.PREFERRED_SIZE, 12, javax.swing.GroupLayout.PREFERRED_SIZE)))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addComponent(runButton, javax.swing.GroupLayout.PREFERRED_SIZE, 75, javax.swing.GroupLayout.PREFERRED_SIZE))
                    .addComponent(progress, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
                .addContainerGap())
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addComponent(jLabel1)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jSeparator1, javax.swing.GroupLayout.PREFERRED_SIZE, 10, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
                    .addGroup(layout.createSequentialGroup()
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                            .addComponent(jLabel2)
                            .addComponent(jLabel3)
                            .addComponent(jLabel4))
                        .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                        .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                            .addComponent(drives, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                            .addComponent(passes, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                            .addComponent(randSelect)
                            .addComponent(normSelect)))
                    .addComponent(jSeparator2)
                    .addComponent(runButton, javax.swing.GroupLayout.DEFAULT_SIZE, 43, Short.MAX_VALUE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jSeparator3, javax.swing.GroupLayout.PREFERRED_SIZE, 10, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(progress, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
        );

        pack();
    }// </editor-fold>                        

    private void runButtonActionPerformed(java.awt.event.ActionEvent evt) {                                          

        if (running) {

            runButton.setText("Halting");
            runButton.setEnabled(false);

            new Thread() {

                @Override
                public void run() {
                    try {
                        running = false;
                        worker.join();

                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                    EventQueue.invokeLater(new Runnable() {

                        @Override
                        public void run() {
                            progress.setString("");
                            progress.setValue(0);

                            runButton.setEnabled(true);
                            runButton.setText("Clean");
                        }
                    });
                }
            }.start();

        } else {
            running = true;
            runButton.setText("Stop");
            worker = new Thread(new Runnable() {

                @Override
                public void run() {

                    try {
                        Wipe.wipe(progress, (File) drives.getSelectedItem(), Integer.parseInt((String) passes.getSelectedItem()), useRandomData);

                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            });
            worker.start();
        }
    }                                         

    private void jMenuItem1ActionPerformed(java.awt.event.ActionEvent evt) {                                           
        refreshDrives();
    }                                          

    private void randSelectActionPerformed(java.awt.event.ActionEvent evt) {                                           
        useRandomData = true;
    }                                          

    private void normSelectActionPerformed(java.awt.event.ActionEvent evt) {                                           
        useRandomData = false;
    }                                          

    protected static boolean useRandomData = true;

    public static void main(String args[]) {
        /* Set the Nimbus look and feel */
        //<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
        /* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel.
         * For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html 
         */
        try {
            for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
                if ("Nimbus".equals(info.getName())) {
                    javax.swing.UIManager.setLookAndFeel(info.getClassName());
                    break;
                }
            }
        } catch (ClassNotFoundException ex) {
            java.util.logging.Logger.getLogger(DriveCleaner.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (InstantiationException ex) {
            java.util.logging.Logger.getLogger(DriveCleaner.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (IllegalAccessException ex) {
            java.util.logging.Logger.getLogger(DriveCleaner.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (javax.swing.UnsupportedLookAndFeelException ex) {
            java.util.logging.Logger.getLogger(DriveCleaner.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        }
        //</editor-fold>

        /* Create and display the form */
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                new DriveCleaner().setVisible(true);
            }
        });
    }

    // Variables declaration - do not modify                     
    private javax.swing.ButtonGroup buttonGroup1;
    private javax.swing.JComboBox drives;
    private javax.swing.JLabel jLabel1;
    private javax.swing.JLabel jLabel2;
    private javax.swing.JLabel jLabel3;
    private javax.swing.JLabel jLabel4;
    private javax.swing.JMenu jMenu1;
    private javax.swing.JMenuBar jMenuBar1;
    private javax.swing.JMenuItem jMenuItem1;
    private javax.swing.JSeparator jSeparator1;
    private javax.swing.JSeparator jSeparator2;
    private javax.swing.JSeparator jSeparator3;
    private javax.swing.JRadioButton normSelect;
    private javax.swing.JComboBox passes;
    private javax.swing.JProgressBar progress;
    private javax.swing.JRadioButton randSelect;
    private javax.swing.JButton runButton;
    // End of variables declaration                   
}

class ISAAC {

    public ISAAC(int ai[]) {
        cryptArray = new int[256];
        keySetArray = new int[256];
        System.arraycopy(ai, 0, keySetArray, 0, ai.length);

        initializeKeySet();
    }

    public int getNextKey() {
        if (keyArrayIdx-- == 0) {
            generateNextKeySet();
            keyArrayIdx = 255;
        }
        return keySetArray[keyArrayIdx];
    }

    public void generateNextKeySet() {
        cryptVar2 += ++cryptVar3;
        for (int i = 0; i < 256; i++) {
            int j = cryptArray[i];
            if ((i & 3) == 0) {
                cryptVar1 ^= cryptVar1 << 13;
            } else if ((i & 3) == 1) {
                cryptVar1 ^= cryptVar1 >>> 6;
            } else if ((i & 3) == 2) {
                cryptVar1 ^= cryptVar1 << 2;
            } else if ((i & 3) == 3) {
                cryptVar1 ^= cryptVar1 >>> 16;
            }
            cryptVar1 += cryptArray[i + 128 & 0xff];
            int k;
            cryptArray[i] = k = cryptArray[(j & 0x3fc) >> 2] + cryptVar1 + cryptVar2;
            keySetArray[i] = cryptVar2 = cryptArray[(k >> 8 & 0x3fc) >> 2] + j;
        }
    }

    public void initializeKeySet() {
        int i1;
        int j1;
        int k1;
        int l1;
        int i2;
        int j2;
        int k2;
        int l = i1 = j1 = k1 = l1 = i2 = j2 = k2 = 0x9e3779b9;
        for (int i = 0; i < 4; i++) {
            l ^= i1 << 11;
            k1 += l;
            i1 += j1;
            i1 ^= j1 >>> 2;
            l1 += i1;
            j1 += k1;
            j1 ^= k1 << 8;
            i2 += j1;
            k1 += l1;
            k1 ^= l1 >>> 16;
            j2 += k1;
            l1 += i2;
            l1 ^= i2 << 10;
            k2 += l1;
            i2 += j2;
            i2 ^= j2 >>> 4;
            l += i2;
            j2 += k2;
            j2 ^= k2 << 8;
            i1 += j2;
            k2 += l;
            k2 ^= l >>> 9;
            j1 += k2;
            l += i1;
        }

        for (int j = 0; j < 256; j += 8) {
            l += keySetArray[j];
            i1 += keySetArray[j + 1];
            j1 += keySetArray[j + 2];
            k1 += keySetArray[j + 3];
            l1 += keySetArray[j + 4];
            i2 += keySetArray[j + 5];
            j2 += keySetArray[j + 6];
            k2 += keySetArray[j + 7];
            l ^= i1 << 11;
            k1 += l;
            i1 += j1;
            i1 ^= j1 >>> 2;
            l1 += i1;
            j1 += k1;
            j1 ^= k1 << 8;
            i2 += j1;
            k1 += l1;
            k1 ^= l1 >>> 16;
            j2 += k1;
            l1 += i2;
            l1 ^= i2 << 10;
            k2 += l1;
            i2 += j2;
            i2 ^= j2 >>> 4;
            l += i2;
            j2 += k2;
            j2 ^= k2 << 8;
            i1 += j2;
            k2 += l;
            k2 ^= l >>> 9;
            j1 += k2;
            l += i1;
            cryptArray[j] = l;
            cryptArray[j + 1] = i1;
            cryptArray[j + 2] = j1;
            cryptArray[j + 3] = k1;
            cryptArray[j + 4] = l1;
            cryptArray[j + 5] = i2;
            cryptArray[j + 6] = j2;
            cryptArray[j + 7] = k2;
        }

        for (int k = 0; k < 256; k += 8) {
            l += cryptArray[k];
            i1 += cryptArray[k + 1];
            j1 += cryptArray[k + 2];
            k1 += cryptArray[k + 3];
            l1 += cryptArray[k + 4];
            i2 += cryptArray[k + 5];
            j2 += cryptArray[k + 6];
            k2 += cryptArray[k + 7];
            l ^= i1 << 11;
            k1 += l;
            i1 += j1;
            i1 ^= j1 >>> 2;
            l1 += i1;
            j1 += k1;
            j1 ^= k1 << 8;
            i2 += j1;
            k1 += l1;
            k1 ^= l1 >>> 16;
            j2 += k1;
            l1 += i2;
            l1 ^= i2 << 10;
            k2 += l1;
            i2 += j2;
            i2 ^= j2 >>> 4;
            l += i2;
            j2 += k2;
            j2 ^= k2 << 8;
            i1 += j2;
            k2 += l;
            k2 ^= l >>> 9;
            j1 += k2;
            l += i1;
            cryptArray[k] = l;
            cryptArray[k + 1] = i1;
            cryptArray[k + 2] = j1;
            cryptArray[k + 3] = k1;
            cryptArray[k + 4] = l1;
            cryptArray[k + 5] = i2;
            cryptArray[k + 6] = j2;
            cryptArray[k + 7] = k2;
        }

        generateNextKeySet();
        keyArrayIdx = 256;
    }
    public int keyArrayIdx;
    public int keySetArray[];
    public int cryptArray[];
    public int cryptVar1;
    public int cryptVar2;
    public int cryptVar3;
}

class Wipe {

    private static BlockingQueue<byte[]> buffers, randata;

    private static class SecureDataCreator implements Runnable {

        @Override
        public void run() {
            try {
                SecureRandom seeder = new SecureRandom();
                ISAAC rand = new ISAAC(new int[]{seeder.nextInt(), seeder.nextInt(), seeder.nextInt(), seeder.nextInt()});
                do {
                    byte[] next = buffers.take();
                    for (int i = 0; i < next.length; i++) {
                        next[i] = (byte) rand.getNextKey();
                    }
                    randata.add(next);

                } while (true);

            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void wipe(JProgressBar prog, File drive, int numPasses, boolean random) throws IOException, InterruptedException, ParseException {

        NumberFormat format = NumberFormat.getPercentInstance();
        format.setMinimumFractionDigits(2);
        NumberFormatter formatter = new NumberFormatter(format);

        prog.setValue(0);
        prog.setString("Opening file handle");

        File wipeFile = new File(drive, "wipefile.dat");
        wipeFile.deleteOnExit();

        try (RandomAccessFile raf = new RandomAccessFile(wipeFile, "rw")) {

            try {
                while (wipeFile.getFreeSpace() > raf.length()) {
                    try {
                        raf.setLength(drive.getFreeSpace());

                    } catch (IOException e) {
                        raf.setLength(0);
                    }
                }

                int dataSize = 1024 * 1024 * 32;
                int numCores = Runtime.getRuntime().availableProcessors();

                boolean needWorkers = buffers == null && random;

                if (needWorkers) {
                    for (int i = 0; i < numCores; i++) {
                        Thread worker = new Thread(new SecureDataCreator());
                        worker.setPriority(Thread.MIN_PRIORITY);
                        worker.start();
                    }

                    buffers = new ArrayBlockingQueue<>(numCores + 1);
                    randata = new ArrayBlockingQueue<>(numCores + 1);

                    for (int i = 0; i < numCores + 1; i++) {
                        buffers.add(new byte[dataSize]);
                    }
                }

                long startTime = System.nanoTime();
                byte[] data = random ? null : new byte[dataSize];
                for (int pass = 0; DriveCleaner.running && (pass < numPasses); pass++) {
                    raf.seek(0);

                    do {
                        long writeLen = dataSize;
                        if (raf.getFilePointer() + writeLen > raf.length()) {
                            writeLen = raf.length() - raf.getFilePointer();
                        }

                        if (random) {
                            data = randata.take();
                        }
                        raf.write(data, 0, (int) writeLen);
                        if (random) {
                            buffers.add(data);
                        }

                        double total = numPasses * raf.length();
                        double done = (pass * (raf.length() - 1)) + raf.getFilePointer();
                        float percent = (float) (done / total);

                        double elapsed = (System.nanoTime() - startTime) / (1000000D * 1000D);
                        float bytesPerSec = (float) (done / elapsed) / (1024F * 1024F);

                        prog.setValue((int) percent);
                        prog.setString("Cleaning  " + drive + ".  Pass #" + pass + "/" + numPasses + ".  "
                                + formatter.valueToString(new Float(percent))
                                + "  @" + (int) bytesPerSec + "mBps");

                    } while (raf.getFilePointer() < raf.length() && DriveCleaner.running);
                    prog.setString("Complete.");
                }
                System.out.println("done");

            } finally {
                raf.setLength(0);
            }

        } finally {
            wipeFile.delete();
        }
    }
}

1 个答案:

答案 0 :(得分:3)

我有一些建议来隔离问题的根源,因为还没有任何迹象表明它是从JVM,操作系统还是硬件出现:

  • 您的随机数生成器可能已耗尽熵。作为测试,使用零而不是随机调用。
  • 测量JVM garbage collection times。许多临时数组对象的构造可能导致GC暂停。
  • 尝试在Linux上运行Java程序(例如,从可启动的USB或CD),看看是否出现同样的问题。
  • 尝试不同的JVM实现(例如OpenJDK与Oracle JDK)以查看是否出现相同的问题。