I am looking at memory allocations of an application that writes to sockets, and I see that a lot of allocations are done in SocketOutputStream.socketWrite in InetAddress:
Is there a way do get rid of these allocations?
I am profiling on Windows using Java 1.8.0_40.
Below is the source code of SocketOutputStream from jdk 1.8, I do not see the code that can allocate these objects
private native void socketWrite0(FileDescriptor fd, byte[] b, int off,
int len) throws IOException;
/**
* Writes to the socket with appropriate locking of the
* FileDescriptor.
* #param b the data to be written
* #param off the start offset in the data
* #param len the number of bytes that are written
* #exception IOException If an I/O error has occurred.
*/
private void socketWrite(byte b[], int off, int len) throws IOException {
if (len <= 0 || off < 0 || off + len > b.length) {
if (len == 0) {
return;
}
throw new ArrayIndexOutOfBoundsException();
}
FileDescriptor fd = impl.acquireFD();
try {
socketWrite0(fd, b, off, len);
} catch (SocketException se) {
if (se instanceof sun.net.ConnectionResetException) {
impl.setConnectionResetPending();
se = new SocketException("Connection reset");
}
if (impl.isClosedOrPending()) {
throw new SocketException("Socket closed");
} else {
throw se;
}
} finally {
impl.releaseFD();
}
}
Related
How can I decode the UTF-8 codepoints of a MappedByteBuffer in Java without copying the buffer? Copying the buffer into memory would defeat the point of mapping the memory, and since CharsetDecoder would require me to copy the buffer with the toCharArray method, it would defeat the purpose of mapping the memory. Is there any way to efficiently decode it without copying the buffer?
Here is a somewhat more complete answer.
It reads the Input-File in Chunks & stores the result in a CharBuffer.
import static java.nio.charset.StandardCharsets.UTF_8;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CoderResult;
import java.nio.charset.MalformedInputException;
import java.nio.charset.UnmappableCharacterException;
import java.nio.file.Files;
import java.nio.file.Path;
public class ChunkedUtf8Decoder {
/*
* Valid UTF-8 Multi-Bytes
* -----------------------
* 1-Byte : 0xxxxxxx
* 2-Byte : 110xxxxx + 10xxxxxx
* 3-Byte : 1110xxxx + 10xxxxxx + 10xxxxxx
* 4-Byte : 11110xxx + 10xxxxxx + 10xxxxxx + 10xxxxxx
*/
private static final int UTF8_CONTINUE_MASK = 0b11_000000;
private static final int UTF8_INVALID_MASK = 0b11111_000;
private static final int UTF8_CONTINUE_PREFIX = 0b10_000000;
private static final int UTF8_INVALID_PREFIX = UTF8_INVALID_MASK;
private static final int CHUNK_SIZE = 24; // TODO Test value! Try something larger, e.g. 64_000
private final CharsetDecoder utf8Decoder = UTF_8.newDecoder();
private final ByteBuffer bb = ByteBuffer.allocate(CHUNK_SIZE); // TODO maybe allocateDirect?
private final CharBuffer cb;
private final Path inputPath;
private byte[] remainder = {};
private int bytesChunked = 0;
public ChunkedUtf8Decoder(final Path inputPath) throws IOException {
this.inputPath = inputPath;
this.cb = CharBuffer.allocate(Math.toIntExact(Files.size(inputPath)));
// this.utf8Decoder.onMalformedInput (CodingErrorAction.REPLACE); // (TODO Default is REPORT)
// this.utf8Decoder.onUnmappableCharacter(CodingErrorAction.REPLACE); // (TODO Default is REPORT)
}
/**
* Split the Input-File into Chunks & Decode them, appending the result to our CharBuffer.
*
* #throws IOException
*/
public void decode() throws IOException {
try(final FileChannel channel = FileChannel.open(inputPath))
{
while (channel.read(bb) != -1) {
debugBytesIn("Read......:", false);
this.remainder = backupToCharBoundary();
debugBytesIn("Backed up.:", true);
this.bytesChunked += decodeChunk();
bb.clear(); // (position=0, limit=capacity, mark=-1)
bb.put(this.remainder); // (Final Remainder is dealt with below)
}
debugBytesIn("EOF.......:", false);
/*
* Lastly, deal with Final Remainder (0 to 4 Bytes) # start of Buffer...
*/
decodeChunk();
}
}
/**
* We try to back up the BB to a Character boundary.
* If the file is correctly encoded, the shorter BB will then decode OK.
* The Remainder will be processed after the NEXT Read operation.
*
* #return
* #throws MalformedInputException
*/
private byte[] backupToCharBoundary() throws MalformedInputException {
for (int i = 1; i <= 4; i++) {
final int trailingBytePos = bb.position() - i;
if (trailingBytePos < 0) {
/*
* If there were too few Bytes available, carry them over until either
* a) more Bytes become available, or
* b) EOF is reached
*/
final byte[] remainder = new byte[bb.position()];
bb.get (0, remainder);
bb.clear(); // (position=0, limit=capacity, mark=-1)
return remainder; // (Entire contents of BB in Remainder, BB Empty)
}
final int trailingByte = 0xFF & bb.get(trailingBytePos);
/*
* We stop as soon as we encounter a Character-Selector...
* (The following 2 intervals are either invalid or a continuation-character)
*/
if ((trailingByte & UTF8_INVALID_MASK ) != UTF8_INVALID_PREFIX // 11111xxx
&& (trailingByte & UTF8_CONTINUE_MASK) != UTF8_CONTINUE_PREFIX) { // 10xxxxxx
/*
* OK, we have found a (1-, 2-, 3- or 4-Byte) Character-Selector...
*/
final byte[] remainder = new byte[i];
bb.get (trailingBytePos, remainder);
bb.position(trailingBytePos);
return remainder; // (Trailing 1-4 Bytes of BB in Remainder)
}
}
/*
* Although at least 4 Bytes are available, We couldn't find a Character-Selector!
* This is an error.
* We leave the ByteBuffer unchanged & return an empty Remainder.
* The CharsetDecoder will produce a MalformedInputException in due course...
*/
return new byte[0];
}
/**
* Decode the current Chunk of Bytes & append the result to our CB.
*
* #return
* #throws CharacterCodingException
*/
private int decodeChunk() throws CharacterCodingException {
try {
this.bb.flip(); // (limit=position, position=0, mark=-1)
; this.utf8Decoder.reset();
final CoderResult res = this.utf8Decoder.decode(this.bb, this.cb, true);
System.out.println("CB Chars..: " + this.cb.position());
/*
* Why on earth is UNDERFLOW the correct result of an error-free Decode?!
*/
if (res.isUnderflow() == false) {
res.throwException();
}
return this.bb.position();
}
catch (final MalformedInputException e) {
throw new MalformedInputException (this.bytesChunked + e.getInputLength());
}
catch (final UnmappableCharacterException e) {
throw new UnmappableCharacterException(this.bytesChunked + e.getInputLength());
/*
* (Famous Last Words "UnmappableCharacterException never occurs with UTF-8 Decoder")
*/
}
catch (final CharacterCodingException inputUnderflowOrOutputOverflow) {
throw inputUnderflowOrOutputOverflow;
/*
* (Underflow if the final Character of the final Chunk was incomplete)
* (Overflow if the Output Buffer is too small, which SHOULD NOT HAPPEN with our big CB)
*/
}
}
/**
* Some Debug Writes.
*
* #param debugText
* #param includeRemainder
*/
private void debugBytesIn(final String debugText, final boolean includeRemainder) {
System .out.print(debugText + " previous=" + this.bytesChunked + " bbPos=" + bb.position() + " Bytes.:");
for ( int p = 0; p < bb.position(); p++) {
System .out.print(" " + Integer.toHexString(0xFF & bb.get(p)));
}
if (includeRemainder) {
System .out.print(" Remainder.:");
for (int r = 0; r < this.remainder.length; r++) {
System.out.print(" " + Integer.toHexString(0xFF & this.remainder[r]));
}
}
System .out.println();
}
public static void main(final String[] args) throws IOException {
final ChunkedUtf8Decoder utf8Decoder = new ChunkedUtf8Decoder(Path.of("Utf8-Chars_nn.txt"));
; utf8Decoder.decode();
System.out.println (utf8Decoder.cb.flip()); // TODO Note.: flip()
}
}
Not really: essentially a char[] needs to be built from the byte[] (either direct or indirect) which is backing the MappedByteBuffer.
So something like the following is necessary:
private static CharBuffer readUTF8(final Path path) throws IOException {
final long byteCount = path.toFile().length();
final FileChannel channel = FileChannel.open(PATH);
final MappedByteBuffer byteBuffer = channel.map(MapMode.READ_ONLY, 0, byteCount);
return StandardCharsets.UTF_8.decode(byteBuffer);
}
The following snippet demonstrates the possibility to reuse various components used in the above example.
As stated in the Javadoc, this is a complex matter.
So it should only be considered as a collection of ideas & invocations of various JDK methods, which may or may not be of use, depending on your concrete requirements.
You really need an in-depth understanding of Charsets etc. before using the following...
/**
* It is possible to pre-allocate a CharsetDecoder & CharBuffer & use those multiple times.
* Be aware that this would make your logic MUCH more complicated.
* a) you'll have to control use/reuse & initialisation of the CharsetDecoder
* b) you'll need to ensure the CharBuffer has sufficient capacity
*
* The following is just snippets which may be of use.
*/
public static void main(final String[] args) throws Exception {
final CharsetDecoder utf8Decoder = StandardCharsets.UTF_8.newDecoder();
; utf8Decoder.onMalformedInput (CodingErrorAction.REPLACE);
; utf8Decoder.onUnmappableCharacter(CodingErrorAction.REPLACE);
final int charBufLength = 0; // TODO how long?
final CharBuffer charBuf = CharBuffer.allocate(charBufLength);
final int byteBufferLength = 0; // TODO how long?
final MappedByteBuffer byteBuffer = FileChannel.open(Path.of("my File")).map(MapMode.READ_ONLY, 0, byteBufferLength);
readUTF8(utf8Decoder, byteBuffer, charBuf);
}
private static void readUTF8(final CharsetDecoder utf8Decoder, final MappedByteBuffer byteBuffer, final CharBuffer charBuffer) throws IOException {
/* Maybe */ utf8Decoder.reset(); // TODO check out the source of the following decode(byteBuffer) method
/* Maybe */ utf8Decoder.decode(byteBuffer); // TODO check out the source of this method
utf8Decoder.decode(byteBuffer, charBuffer, true); // reuses utf8Decoder & charBuffer
charBuffer.flip();
}
I am using boost crc32 to simluate java version:
long HashUtils::CalcCRC32(const std::wstring& filePath)
{
HANDLE hFile = CreateFile(filePath.c_str(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
if (hFile == INVALID_HANDLE_VALUE)
return 0;
std::vector<byte> buff;
DWORD dwSize = GetFileSize(hFile, NULL);
buff.resize(dwSize);
boost::crc_32_type crc32;
DWORD bytesRead = 0;
if (!ReadFile(hFile, buff.data(), buff.size(), &bytesRead, 0) || bytesRead != dwSize) {
CloseHandle(hFile);
return 0;
}
CloseHandle(hFile);
crc32.process_block(buff.data(), buff.data() + buff.size());
return crc32.checksum();
}
The java version
private static long calcCRC32(byte[] raw, int offset, int len) throws IOException {
CRC32 crc32 = new CRC32();
crc32.update(raw, offset, len);
return crc32.getValue();
}
The problem is java version return long, which is 64bit, while the c++/boost version return long which is 32bit. Will the crc32 result of the java version overflow 32bit?
The java CRC32 class only returns 32 bit CRCs. Therefore the int datatype is large enough to hold all the information. You can rewrite your function as this:
private static int calcCRC32(byte[] raw, int offset, int len) throws IOException
{
CRC32 crc32 = new CRC32();
crc32.update(raw, offset, len);
return (int)crc32.getValue();
}
Now you should have similar results from the C++ and java version.
The reason that crc.getValue() returns a long instead of an int is quite simple by the way: CRC32 implements the Checksum interface. This interface has been specified to return long for the getValue() method. This was done to allow for checksum implementations that return more than 32 useful bits of information.
Hi Team, I am trying to find a String "Henry" in a binary file and change the String to a different string. FYI the file is the output of serialisation of an object. Original Question here
I am new to searching bytes and imagined this code would search for my byte[] and exchange it. But it doesn't come close to working it doesn't even find a match.
{
byte[] bytesHenry = new String("Henry").getBytes();
byte[] bytesSwap = new String("Zsswd").getBytes();
byte[] seekHenry = new byte[bytesHenry.length];
RandomAccessFile file = new RandomAccessFile(fileString,"rw");
long filePointer;
while (seekHenry != null) {
filePointer = file.getFilePointer();
file.readFully(seekHenry);
if (bytesHenry == seekHenry) {
file.seek(filePointer);
file.write(bytesSwap);
break;
}
}
}
Okay I see the bytesHenry==seekHenry problem and will swap to Arrays.equals( bytesHenry , seekHenry )
I think I need to move along by -4 byte positions each time i read 5 bytes.
Bingo it finds it now
while (seekHenry != null) {
filePointer = file.getFilePointer();
file.readFully(seekHenry);;
if (Arrays.equals(bytesHenry,
seekHenry)) {
file.seek(filePointer);
file.write(bytesSwap);
break;
}
file.seek(filePointer);
file.read();
}
The following could work for you, see the method search(byte[] input, byte[] searchedFor) which returns the index where the first match matches, or -1.
public class SearchBuffer {
public static void main(String[] args) throws UnsupportedEncodingException {
String charset= "US-ASCII";
byte[] searchedFor = "ciao".getBytes(charset);
byte[] input = "aaaciaaaciaojjcia".getBytes(charset);
int idx = search(input, searchedFor);
System.out.println("index: "+idx); //should be 8
}
public static int search(byte[] input, byte[] searchedFor) {
//convert byte[] to Byte[]
Byte[] searchedForB = new Byte[searchedFor.length];
for(int x = 0; x<searchedFor.length; x++){
searchedForB[x] = searchedFor[x];
}
int idx = -1;
//search:
Deque<Byte> q = new ArrayDeque<Byte>(input.length);
for(int i=0; i<input.length; i++){
if(q.size() == searchedForB.length){
//here I can check
Byte[] cur = q.toArray(new Byte[]{});
if(Arrays.equals(cur, searchedForB)){
//found!
idx = i - searchedForB.length;
break;
} else {
//not found
q.pop();
q.addLast(input[i]);
}
} else {
q.addLast(input[i]);
}
}
return idx;
}
}
From Fastest way to find a string in a text file with java:
The best realization I've found in MIMEParser: https://github.com/samskivert/ikvm-openjdk/blob/master/build/linux-amd64/impsrc/com/sun/xml/internal/org/jvnet/mimepull/MIMEParser.java
/**
* Finds the boundary in the given buffer using Boyer-Moore algo.
* Copied from java.util.regex.Pattern.java
*
* #param mybuf boundary to be searched in this mybuf
* #param off start index in mybuf
* #param len number of bytes in mybuf
*
* #return -1 if there is no match or index where the match starts
*/
private int match(byte[] mybuf, int off, int len) {
Needed also:
private void compileBoundaryPattern();
I would like to create a version of Java's deflater not synchronizing on its ZStreamRef instance.
I started by copying the code into a Deflater2 class. The code compiles, however, when I create an instance of that class, I get:
Exception in thread "main" java.lang.UnsatisfiedLinkError: net.dwst.bricolo.ResetGZip.Deflater2.initIDs()V
at net.mypackage.Deflater2.initIDs(Native Method)
at net.mypackage.Deflater2.<clinit>(Deflater2.java:65)
at net.mypackage.SpeedTest.main(SpeedTest.java:13)
How can I create an instance of Deflater2 without triggering this exception?
I am providing the code for Deflater2:
package net.mypackage;
public class Deflater2 {
private final ZStreamRef zsRef;
private byte[] buf = new byte[0];
private int off, len;
private int level, strategy;
private boolean setParams;
private boolean finish, finished;
/**
* Compression method for the deflate algorithm (the only one currently
* supported).
*/
public static final int DEFLATED = 8;
/**
* Compression level for no compression.
*/
public static final int NO_COMPRESSION = 0;
/**
* Compression level for fastest compression.
*/
public static final int BEST_SPEED = 1;
/**
* Compression level for best compression.
*/
public static final int BEST_COMPRESSION = 9;
/**
* Default compression level.
*/
public static final int DEFAULT_COMPRESSION = -1;
/**
* Compression strategy best used for data consisting mostly of small
* values with a somewhat random distribution. Forces more Huffman coding
* and less string matching.
*/
public static final int FILTERED = 1;
/**
* Compression strategy for Huffman coding only.
*/
public static final int HUFFMAN_ONLY = 2;
/**
* Default compression strategy.
*/
public static final int DEFAULT_STRATEGY = 0;
static {
/* Zip library is loaded from System.initializeSystemClass */
initIDs();
}
/**
* Creates a new compressor using the specified compression level.
* If 'nowrap' is true then the ZLIB header and checksum fields will
* not be used in order to support the compression format used in
* both GZIP and PKZIP.
* #param level the compression level (0-9)
* #param nowrap if true then use GZIP compatible compression
*/
public Deflater2(int level, boolean nowrap) {
this.level = level;
this.strategy = DEFAULT_STRATEGY;
this.zsRef = new ZStreamRef(init(level, DEFAULT_STRATEGY, nowrap));
}
/**
* Creates a new compressor using the specified compression level.
* Compressed data will be generated in ZLIB format.
* #param level the compression level (0-9)
*/
public Deflater2(int level) {
this(level, false);
}
/**
* Creates a new compressor with the default compression level.
* Compressed data will be generated in ZLIB format.
*/
public Deflater2() {
this(DEFAULT_COMPRESSION, false);
}
/**
* Sets input data for compression. This should be called whenever
* needsInput() returns true indicating that more input data is required.
* #param b the input data bytes
* #param off the start offset of the data
* #param len the length of the data
* #see Deflater#needsInput
*/
public void setInput(byte[] b, int off, int len) {
if (b== null) {
throw new NullPointerException();
}
if (off < 0 || len < 0 || off > b.length - len) {
throw new ArrayIndexOutOfBoundsException();
}
synchronized (zsRef) {
this.buf = b;
this.off = off;
this.len = len;
}
}
/**
* Sets input data for compression. This should be called whenever
* needsInput() returns true indicating that more input data is required.
* #param b the input data bytes
* #see Deflater#needsInput
*/
public void setInput(byte[] b) {
setInput(b, 0, b.length);
}
/**
* Sets preset dictionary for compression. A preset dictionary is used
* when the history buffer can be predetermined. When the data is later
* uncompressed with Inflater.inflate(), Inflater.getAdler() can be called
* in order to get the Adler-32 value of the dictionary required for
* decompression.
* #param b the dictionary data bytes
* #param off the start offset of the data
* #param len the length of the data
* #see Inflater#inflate
* #see Inflater#getAdler
*/
public void setDictionary(byte[] b, int off, int len) {
if (b == null) {
throw new NullPointerException();
}
if (off < 0 || len < 0 || off > b.length - len) {
throw new ArrayIndexOutOfBoundsException();
}
synchronized (zsRef) {
ensureOpen();
setDictionary(zsRef.address(), b, off, len);
}
}
/**
* Sets preset dictionary for compression. A preset dictionary is used
* when the history buffer can be predetermined. When the data is later
* uncompressed with Inflater.inflate(), Inflater.getAdler() can be called
* in order to get the Adler-32 value of the dictionary required for
* decompression.
* #param b the dictionary data bytes
* #see Inflater#inflate
* #see Inflater#getAdler
*/
public void setDictionary(byte[] b) {
setDictionary(b, 0, b.length);
}
/**
* Sets the compression strategy to the specified value.
* #param strategy the new compression strategy
* #exception IllegalArgumentException if the compression strategy is
* invalid
*/
public void setStrategy(int strategy) {
switch (strategy) {
case DEFAULT_STRATEGY:
case FILTERED:
case HUFFMAN_ONLY:
break;
default:
throw new IllegalArgumentException();
}
synchronized (zsRef) {
if (this.strategy != strategy) {
this.strategy = strategy;
setParams = true;
}
}
}
/**
* Sets the current compression level to the specified value.
* #param level the new compression level (0-9)
* #exception IllegalArgumentException if the compression level is invalid
*/
public void setLevel(int level) {
if ((level < 0 || level > 9) && level != DEFAULT_COMPRESSION) {
throw new IllegalArgumentException("invalid compression level");
}
synchronized (zsRef) {
if (this.level != level) {
this.level = level;
setParams = true;
}
}
}
/**
* Returns true if the input data buffer is empty and setInput()
* should be called in order to provide more input.
* #return true if the input data buffer is empty and setInput()
* should be called in order to provide more input
*/
public boolean needsInput() {
return len <= 0;
}
/**
* When called, indicates that compression should end with the current
* contents of the input buffer.
*/
public void finish() {
synchronized (zsRef) {
finish = true;
}
}
/**
* Returns true if the end of the compressed data output stream has
* been reached.
* #return true if the end of the compressed data output stream has
* been reached
*/
public boolean finished() {
synchronized (zsRef) {
return finished;
}
}
/**
* Fills specified buffer with compressed data. Returns actual number
* of bytes of compressed data. A return value of 0 indicates that
* needsInput() should be called in order to determine if more input
* data is required.
* #param b the buffer for the compressed data
* #param off the start offset of the data
* #param len the maximum number of bytes of compressed data
* #return the actual number of bytes of compressed data
*/
public int deflate(byte[] b, int off, int len) {
if (b == null) {
throw new NullPointerException();
}
if (off < 0 || len < 0 || off > b.length - len) {
throw new ArrayIndexOutOfBoundsException();
}
synchronized (zsRef) {
ensureOpen();
return deflateBytes(zsRef.address(), b, off, len);
}
}
/**
* Fills specified buffer with compressed data. Returns actual number
* of bytes of compressed data. A return value of 0 indicates that
* needsInput() should be called in order to determine if more input
* data is required.
* #param b the buffer for the compressed data
* #return the actual number of bytes of compressed data
*/
public int deflate(byte[] b) {
return deflate(b, 0, b.length);
}
/**
* Returns the ADLER-32 value of the uncompressed data.
* #return the ADLER-32 value of the uncompressed data
*/
public int getAdler() {
synchronized (zsRef) {
ensureOpen();
return getAdler(zsRef.address());
}
}
/**
* Returns the total number of uncompressed bytes input so far.
*
* <p>Since the number of bytes may be greater than
* Integer.MAX_VALUE, the {#link #getBytesRead()} method is now
* the preferred means of obtaining this information.</p>
*
* #return the total number of uncompressed bytes input so far
*/
public int getTotalIn() {
return (int) getBytesRead();
}
/**
* Returns the total number of uncompressed bytes input so far.</p>
*
* #return the total (non-negative) number of uncompressed bytes input so far
* #since 1.5
*/
public long getBytesRead() {
synchronized (zsRef) {
ensureOpen();
return getBytesRead(zsRef.address());
}
}
/**
* Returns the total number of compressed bytes output so far.
*
* <p>Since the number of bytes may be greater than
* Integer.MAX_VALUE, the {#link #getBytesWritten()} method is now
* the preferred means of obtaining this information.</p>
*
* #return the total number of compressed bytes output so far
*/
public int getTotalOut() {
return (int) getBytesWritten();
}
/**
* Returns the total number of compressed bytes output so far.</p>
*
* #return the total (non-negative) number of compressed bytes output so far
* #since 1.5
*/
public long getBytesWritten() {
synchronized (zsRef) {
ensureOpen();
return getBytesWritten(zsRef.address());
}
}
/**
* Resets deflater so that a new set of input data can be processed.
* Keeps current compression level and strategy settings.
*/
public void reset() {
synchronized (zsRef) {
ensureOpen();
reset(zsRef.address());
finish = false;
finished = false;
off = len = 0;
}
}
/**
* Closes the compressor and discards any unprocessed input.
* This method should be called when the compressor is no longer
* being used, but will also be called automatically by the
* finalize() method. Once this method is called, the behavior
* of the Deflater object is undefined.
*/
public void end() {
synchronized (zsRef) {
long addr = zsRef.address();
zsRef.clear();
if (addr != 0) {
end(addr);
buf = null;
}
}
}
/**
* Closes the compressor when garbage is collected.
*/
protected void finalize() {
end();
}
private void ensureOpen() {
assert Thread.holdsLock(zsRef);
if (zsRef.address() == 0)
throw new NullPointerException("Deflater has been closed");
}
private static native void initIDs();
private native static long init(int level, int strategy, boolean nowrap);
private native static void setDictionary(long addr, byte[] b, int off,
int len);
private native int deflateBytes(long addr, byte[] b, int off, int len);
private native static int getAdler(long addr);
private native static long getBytesRead(long addr);
private native static long getBytesWritten(long addr);
private native static void reset(long addr);
private native static void end(long addr);
}
Your problem is not in java code.
There can be the following reasons:
the library that you are using or other library that your library depends on is not in library path (java.library.path option)
One of the native libraries you try to use does not match your platforms. For example you are running on 32 bit platform and trying to use 64bit library.
Take a look here: How to add native library to "java.library.path" with Eclipse launch (instead of overriding it)
I saw the following description in the official tomcat configuration documentation (APR connector description omitted):
Java Blocking Connector Java Nio Blocking Connector
Classname Http11Protocol Http11NioProtocol
Tomcat Version 3.x 4.x 5.x 6.x 6.x
Support Polling NO YES
Polling Size N/A Unlimited - Restricted by mem
Read HTTP Request Blocking Non Blocking
Read HTTP Body Blocking Sim Blocking
Write HTTP Response Blocking Sim Blocking
SSL Support Java SSL Java SSL
SSL Handshake Blocking Non blocking
Max Connections maxThreads See polling size
What does "Sim Blocking" mean?
According to Filip Hanik, a Tomcat committer, it means "simulated blocking". (Reference: Tomcat User Mailing list post)
Just a guess, but it could stand for simulated blocking, meaning a blocking api wrapped around the underlying non-blocking nio api.
SourceCode: https://github.com/apache/tomcat/blob/8.5.x/java/org/apache/tomcat/util/net/NioEndpoint.java
/**
* NioEndpoint.NioSocketWrapper.fillReadBuffer() 用于直接读取内容到 传入的任意 ByteBuffer 中
*
* #param block 是否阻塞读
* #param buffer 待接收数据的buffer
* #return 读取到的字节数
* #throws IOException
*/
private int fillReadBuffer(boolean block, ByteBuffer buffer) throws IOException {
int n = 0;
if (getSocket() == NioChannel.CLOSED_NIO_CHANNEL) {
throw new ClosedChannelException();
}
if (block) { // if no readListener. block variable is true.
long timeout = getReadTimeout();
long startNanos = 0;
do {
if (startNanos > 0) {
long elapsedMillis = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNanos);
if (elapsedMillis == 0) {
elapsedMillis = 1;
}
timeout -= elapsedMillis;
if (timeout <= 0) {
throw new SocketTimeoutException();
}
}
n = getSocket().read(buffer);
if (n == -1) {
throw new EOFException();
} else if (n == 0) {
if (!readBlocking) {
readBlocking = true;
registerReadInterest(); // <=============== (向关联的Poller)注册读事件.
}
synchronized (readLock) {
if (readBlocking) {
try {
if (timeout > 0) {
startNanos = System.nanoTime();
readLock.wait(timeout); // block itself here < ===================
} else {
readLock.wait();
}
} catch (InterruptedException e) {
// Continue
}
}
}
}
} while (n == 0); // TLS needs to loop as reading zero application bytes is possible
} else {
n = getSocket().read(buffer);
if (n == -1) {
throw new EOFException();
}
}
return n;
}