免费视频淫片aa毛片_日韩高清在线亚洲专区vr_日韩大片免费观看视频播放_亚洲欧美国产精品完整版

打開APP
userphoto
未登錄

開通VIP,暢享免費(fèi)電子書等14項(xiàng)超值服

開通VIP
Java性能調(diào)優(yōu)指南

Java性能調(diào)優(yōu)指南—-java.io.BufferedInputStream和java.util.zip.GZIPInputStream

分享到:14


本文由 ImportNew - 曾柏羲 翻譯自 Java Performance Tuning Guide。如需轉(zhuǎn)載本文,請(qǐng)先參見(jiàn)文章末尾處的轉(zhuǎn)載要求。

Importnew注:如果你也對(duì)Java技術(shù)翻譯分享感興趣,歡迎加入我們的Java開發(fā)小組。參與方式請(qǐng)查看小組簡(jiǎn)介

BufferedInputStreamGZIPInputStream是在讀取文件數(shù)據(jù)中經(jīng)常使用到的兩個(gè)類(至少后者在Linux系統(tǒng)中被廣泛使用)。一般來(lái)說(shuō),緩沖輸入數(shù)據(jù)是一種很好的想法,這在許多關(guān)于Java性能的書籍中都有描述。對(duì)于這些流,仍然有許多問(wèn)題值得我們了解。

 

何時(shí)不需要緩沖

緩沖是用來(lái)減少來(lái)自輸入設(shè)備的單獨(dú)讀取操作數(shù)的數(shù)量,許多開發(fā)者往往忽視這一點(diǎn),并經(jīng)常將InputStream包含進(jìn)BufferedInputStream中,如

1
final InputStream is = new BufferedInputStream( new FileInputStream( file ) );

是否使用緩沖的簡(jiǎn)略規(guī)則如下:當(dāng)你的數(shù)據(jù)塊足夠大的時(shí)候(100K+),你不需要使用緩沖,你可以處理任何長(zhǎng)度的塊(不需要保證在緩沖前緩沖區(qū)中至少有N bytes可用字節(jié))。在所有的其他情況下,你都需要緩沖輸入數(shù)據(jù)。最簡(jiǎn)單的不需要緩沖的例子就是手動(dòng)復(fù)制文件的過(guò)程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public static void copyFile( final File from, final File to ) throws IOException {
    final InputStream is = new FileInputStream( from );
    try
    {
        final OutputStream os = new FileOutputStream( to );
        try
        {
            final byte[] buf = new byte[ 8192 ];
            int read = 0;
            while ( ( read = is.read( buf ) ) != -1 )
            {
                os.write( buf, 0, read );
            }
        }
        finally {
            os.close();
        }
    }
    finally {
        is.close();
    }
}

注1:衡量文件復(fù)制的性能是非常困難的,因?yàn)檫@很大程度上收到操作系統(tǒng)寫入緩存的影響。在我的機(jī)器上復(fù)制一個(gè)4.5G的文件到相同的硬盤所花費(fèi)的時(shí)間在68至107s之間變化。

注2:文件復(fù)制經(jīng)常通過(guò)Java NIO實(shí)現(xiàn),使用FileChannel.transferTo或者transferFrom 的方法。使用這些方法不需要再在內(nèi)核和用戶態(tài)之間頻繁的轉(zhuǎn)換(在用戶的java程序中將讀入數(shù)據(jù)轉(zhuǎn)換為字節(jié)緩存,再通過(guò)內(nèi)核調(diào)用將它復(fù)制回輸出文件中)。相反它們?cè)趦?nèi)核模式中傳輸盡可能多的數(shù)據(jù)(直達(dá)231-1字節(jié)),盡可能做到不返回用戶的代碼中。因此,Java NIO會(huì)使用較少的CPU周期,并騰出留給其他程序。然而,只有高負(fù)荷的環(huán)境下才能看到這當(dāng)中的差異(在我的機(jī)器中,NIO模式的CPU總占用率為4%,而舊的流模式CPU總占用率則為8-9%)。以下是一個(gè)可能的Java NIO實(shí)現(xiàn):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
private static void copyFileNio( final File from, final File to ) throws IOException {
    final RandomAccessFile inFile = new RandomAccessFile( from, "r" );
    try
    {
        final RandomAccessFile outFile = new RandomAccessFile( to, "rw" );
        try
        {
            final FileChannel inChannel = inFile.getChannel();
            final FileChannel outChannel = outFile.getChannel();
            long pos = 0;
            long toCopy = inFile.length();
            while ( toCopy > 0 )
            {
                final long bytes = inChannel.transferTo( pos, toCopy, outChannel );
                pos += bytes;
                toCopy -= bytes;
            }
        }
        finally {
            outFile.close();
        }
    }
    finally {
        inFile.close();
    }
}

 

緩沖大小

BufferedInputStream中默認(rèn)的緩沖大小是8192個(gè)字節(jié)。緩沖大小實(shí)際上是從輸入設(shè)備中準(zhǔn)備讀取的塊的平均大小。這就是為什么它經(jīng)常值得精確地提高至64K(65536), 萬(wàn)一有非常大的輸入文件 — 那些在512K和2M的文件,為了更深一層地減少磁盤讀入的數(shù)量。許多專家也建議將此值設(shè)置為4096的整數(shù)倍 — 一個(gè)普通磁盤扇區(qū)的大小。所以,不要將緩沖區(qū)的大小設(shè)置為,像125000這樣的大小,取而代之的應(yīng)該是像131072(128K)這樣的大小。

java.util.zip.GZIPInputStream 是一個(gè)能夠很好處理gzip文件輸入的輸入流。它經(jīng)常被用來(lái)做這樣的事情:

1
final InputStream is = new GZIPInputStream( new BufferedInputStream( new FileInputStream( file ) ) );

這樣的初始化已經(jīng)足夠好了,不過(guò)BufferedInputStream在此處是多余的,因?yàn)?em>GZIPInputStream已經(jīng)擁有了自己的內(nèi)建緩沖區(qū),它里面有一個(gè)字節(jié)緩沖區(qū)(實(shí)際上,它是InflaterInputStream的成員),被用做此從底層流中讀取壓縮的數(shù)據(jù),并且將其傳遞給一個(gè)inflater。這個(gè)緩沖區(qū)默認(rèn)的大小是512字節(jié),所以它必須被設(shè)置成一個(gè)更高的數(shù)值。一個(gè)更理想地使用GZIPInputStream的方式如下:

1
final InputStream is = new GZIPInputStream( new FileInputStream( file ), 65536 );

 

BufferedInputStream.available

BufferedInputStream.available 方法有一個(gè)可能的性能問(wèn)題,取決于你真正想要接收到的東西。它的標(biāo)準(zhǔn)實(shí)現(xiàn)將返回BufferedInputStream自身的內(nèi)部緩沖區(qū)可用字節(jié)數(shù)和底層輸入流調(diào)用avalibale()結(jié)果的總和。所以,它將盡可能地返回一個(gè)精確的數(shù)值。但是在很多案例中,用戶想知道的僅僅是緩沖區(qū)中是否還有空間可用( available() > 0). 在這種情況下,即使BufferedInputStream的緩沖區(qū)中只有一個(gè)字節(jié)余留,我們都不需要去查詢底層的輸入流。這顯得非常重要,如果我們有一個(gè)FileInputStream包含在BufferedInputStream中–這樣的優(yōu)化會(huì)節(jié)省我們?cè)?em>FileInputStream.available()中的磁盤訪問(wèn)時(shí)間。

幸運(yùn)的是,我們可以簡(jiǎn)單地解決這樣的問(wèn)題。BufferedInputStream不是一個(gè)final類,所以我們可以繼承并且重載available方法。我們可以看看JDK的源代碼著手準(zhǔn)備。從這里我們還可以發(fā)現(xiàn)Java 6中的實(shí)現(xiàn)有一個(gè)bug — 如果BufferedInputStream可用的字節(jié)數(shù)和底層流available()調(diào)用結(jié)果的總和大于Integer.MaxVALUE,這樣就會(huì)因?yàn)橐绯龆祷匾粋€(gè)負(fù)數(shù)結(jié)果,不過(guò)這在Java 7中已經(jīng)得到了解決。

以下是我們改進(jìn)的實(shí)現(xiàn),它將返回BufferedInputStream的內(nèi)置緩沖區(qū)中可用的字節(jié)數(shù),又或者是,如果它里面沒(méi)有剩余的字節(jié)數(shù),底層流中的available()方法會(huì)被調(diào)用,并且返回調(diào)用后的結(jié)果。在大多數(shù)情況下,這種實(shí)現(xiàn)會(huì)極少調(diào)用到底層流中的available()方法,因?yàn)楫?dāng)BufferedInputStream緩沖區(qū)被讀取到最后,這個(gè)類會(huì)讀取從底層流中讀取更多的數(shù)據(jù),所以,我們只會(huì)在輸入文件的末尾中調(diào)用底層流的available()方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
public class FasterBufferedInputStream extends BufferedInputStream
{
    public FasterBufferedInputStream(InputStream in, int size) {
        super(in, size);
    }
    //如果有東西可用,該方法將返回一個(gè)正數(shù),否則它會(huì)返回0。
    public int available() throws IOException {
        if (in == null)
            throw new IOException( "Stream closed" );
        final int n = count - pos;
        return n > 0 ? n : in.available();
    }
}

為了測(cè)試這個(gè)實(shí)現(xiàn),我嘗試使用標(biāo)準(zhǔn)版的和改進(jìn)版的BufferedInputStream去讀取4.5G的文件,它們都有64K的緩沖區(qū)大小,并且每讀取512或者1024字節(jié)的時(shí)候就調(diào)用一次available()方法。干凈的測(cè)試需要操作系統(tǒng)在每一次測(cè)試之后重啟以清除磁盤緩存。于是我決定在熱身階段讀取文件,當(dāng)文件已經(jīng)在磁盤緩存時(shí)就用兩種方法測(cè)試性能。測(cè)試顯示,標(biāo)準(zhǔn)類的運(yùn)行時(shí)間與available()調(diào)用的數(shù)量呈線性關(guān)系。而改進(jìn)的方法運(yùn)行時(shí)間看起來(lái)卻與調(diào)用的次數(shù)無(wú)關(guān)。


standard, once per 512 bytesimproved, once per 512 bytesstandard, once per 1024 bytesimproved, once per 1024 bytes
Java 617.062 sec2.11 sec9.592 sec2.047 sec
Java 717.337 sec2.125 sec9.748 sec2.044 sec

這里是測(cè)試的源代碼:

1
2
3
4
5
6
7
8
9
10
11
private static void testRead( final InputStream is ) throws IOException {
    final long start = System.currentTimeMillis();
    final byte[] buf = new byte[ 512 ];   //or 1024 bytes
    while ( true )
    {
        if ( is.available() == 0 ) break;
        is.read( buf );
    }
    final long time = System.currentTimeMillis() - start;
    System.out.println( "Impl: " + is.getClass().getCanonicalName() + " time = " + time / 1000.0 + " sec");
}
1
2
3
4
使用以下的聲明變量調(diào)用以上方法:
final InputStream is1 = new BufferedInputStream( new FileInputStream( file ), 65536 );
and
final InputStream is2 = new FasterBufferedInputStream( new FileInputStream( file ), 65536 );

 

總結(jié)

  • BufferedInputStream和GZIPInputStream 都有內(nèi)建的緩沖區(qū)。前者默認(rèn)的緩沖大小是8192字節(jié),后者則為512字節(jié)。一般而言,它值得增加任何它們的整數(shù)倍大小到至少65536。
  • 不要使用BufferedInputStream作為GZIPInputStream的輸入,相反,顯示地在構(gòu)造器中設(shè)置GZIPInputStream的緩存大小。雖然,保持BufferedInputStream仍然是安全的。
  • 如果你有一個(gè)new BufferedInputStream( new FileInputStream( file ) )對(duì)象,并且需要頻繁地調(diào)用它的available方法(例如,每輸入一次信息都需要調(diào)用一次或者兩次),考慮重載 BufferedInputStream.available方法,它將極大地提高文件讀取的速度。
本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊舉報(bào)。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
“給你第二次機(jī)會(huì)”——小議PushbackInputStream
BufferedOutputStream 源碼分析
day20
java中輸入輸出總結(jié)
任務(wù)9 數(shù)據(jù)的導(dǎo)入導(dǎo)出
深入理解Java中的流(Stream)
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長(zhǎng)圖 關(guān)注 下載文章
綁定賬號(hào)成功
后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服