Proste obliczanie CRC dla strumienia bajtów + OutOfMemoryError

java debuggingNie wszystko złoto co się świeci. W książce Common Java CookBook jest przedstawiony prosty sposób obliczenia CRC dla pliku przy użyciu biblioteki google guava. Krótki i czytelny kod świetnie sprawdza się dla małych plików. Ale z dużymi niestety nie współpracuje tak jak się tego spodziewamy. Oto lekko przerobiony kod (zmieniłem sposób wskazania na plik) z rozdziału 15.4:

public static void main(final String[] args) throws Exception {
final InputStream test = new FileInputStream(„test.zip”);
final byte[] byteArray = ByteStreams.toByteArray(test);
final CRC32 crc32 = new CRC32();
final long checksum = ByteStreams.getChecksum(
ByteStreams.newInputStreamSupplier(byteArray), crc32);
System.out.printf(„Checksum: %d”, checksum);
}

Dla pliku test.zip o rozmiarze 140 MB wszystko poszło sprawnie i dostałem wynik: Checksum: 1755861318
Dla pliku o rozmiarze 304 MB niestety dostałem wyjątek:

Exception in thread „main” java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:2786)
at java.io.ByteArrayOutputStream.write(ByteArrayOutputStream.java:94)
at com.google.common.io.ByteStreams.copy(ByteStreams.java:171)
at com.google.common.io.ByteStreams.toByteArray(ByteStreams.java:210)
at test.Test.main(Test.java:13)

Typowa reakcja na ten wyjątek to zwiększenie pamięci dla JVM. Zwiększyłem więc pamięć dla Eclipse z 512MB do 1GB, ale nie rozwiązało to problemu. A nawet jeśli w tym przypadku by pomogło, to co by było gdybym obliczał za pomocą tego kodu CRC dla pliku np o wielkości 2GB? Prawdopodobnie ten sam problem.
No więc na koniec morał: używanie zewnętrznych bibliotek ułatwia życie, ponieważ pozwala skrócić czas pisania kodu oraz poprawić jego czytelność. Jednak jak każdy kod, mogą zawierać błędy Bądźcie ostrożni i testujcie Wasz kod
Błąd spowodował ten kawałek kodu: ByteStreams.toByteArray(test);
Zgłosiłem go tutaj: http://code.google.com/p/guava-libraries/issues/detail?id=551

update:

Już po godzinie dostałem pouczającą odpowiedź z www.badania-ottima.com.pl:
This doesn’t have anything to do with ByteStreams.toByteArray. 300 MB is generally too much to be loading into memory, and it isn’t necessary at all in your example. What you should be doing is this:

CRC32 crc32 = new CRC32();
File file = new File(„test.zip”);
long checksum = ByteStreams.getChecksum(
Files.newInputStreamSupplier(file), crc32);

Kod sprawdziłem dla pliku 1,42GB i błyskawicznie dostałem odpowiedź: Checksum: 2642167178

Musi się więc zmienić morał: podchodź nieufnie do kodu znalezionego w książkach, ponieważ błąd może się znajdować w logice, a nie w użytych bibliotekach