In-depth understanding of Okio's optimization ideas

Source: Internet
Author: User
Tags compact

As more and more applications use Okhttp for network access, we need to delve deeper into Okhttp's cornerstone, a lighter and more efficient IO library Okio.

Advantages of Okio

Some students may ask, the current Java io is very mature, why do you want to use the new IO library? The author thinks that the answer has the following points:

    1. Low CPU and memory consumption. We will analyze later that Okio uses the segment mechanism for memory sharing and reuse, as little as possible to apply for memory, and also to reduce the frequency of GC. We know that an overly frequent GC can cause performance problems for your application.
    2. Easy to use. In Okio, the bytestring is provided to handle the invariant byte sequence, which is optimized for memory, from byte[] to string or from string to byte[, and provides tools such as hex characters, base64, and so on. Buffer is a powerful tool for handling variable byte sequences, it can automatically grow according to usage, and does not care about the processing of position and other positions during the use.
    3. N Unity. Java Native Io,inputstream/outputstream, if you need to read data, such as reading an Integer, a Boolean, or a floating point, you need to wrap it in DataInputStream, if you are using it as a cache, in order to be efficient, You need to use Bufferedoutputstream. In the Okio Bufferedsink/bufferedsource has the above basic all functions, does not need to string up a series of adornment class.
    4. Provides a series of convenient tools, such as gzip transparent processing, data calculation MD5, SHA1 and so on provide support, the data verification is very convenient.
Frame design of Okio

Okio is lightweight, his code is very clear. The most important two interfaces are source and sink, respectively.

Source

This interface is mainly used to read data, and the source of data can be disk, network, memory, etc., but also can be extended processing interface, such as decompression, decryption, remove unnecessary network frames.

 Public  interface Source extends closeable {  /** * Removes at least 1, and up to {@code ByteCount} bytes from this and appends * them to {@code sink}.   Returns the number of bytes read, or-1 if this * source is exhausted. */  LongRead (Buffer sink,LongByteCount)throwsIOException;/** Returns The timeout for this source. * /Timeout timeout ();/** * Closes this source and releases the resources held by this source. It is an * error to read a closed source.   It is safe to close a source more than once. */  @Override voidClose ()throwsIOException;}

For the subclass of source, we need to focus on bufferedsource. It is also an interface, but it provides more methods of operation.

And Realbufferedsource is its direct implementation class. Implements all of its interfaces. Their relationship is as follows.

In fact, the implementation of Realbufferedsource is based on the buffer class. We'll talk about this class later.

Sink

Sink is similar to source, except that it writes data.

 Public  interface Sink extends closeable, flushable {  /** removes {@code ByteCount} bytes from {@code source} and appends them to this. * /  voidWrite (Buffer source,LongByteCount)throwsIOException;/** pushes all buffered bytes to their final destination. * *  @Override voidFlush ()throwsIOException;/** Returns The timeout for this sink. * /Timeout timeout ();/** * Pushes all buffered bytes to their final destination and releases the * resources held by this sink. It is an error to write a closed sink.   It is * safe-to-close a sink more than once. */  @Override voidClose ()throwsIOException;}

Similarly, it also has a subclass Bufferedsink, which defines all operations on the data. Its direct class Realbufferedsink is also done using buffer.

Buffer

Buffer is a very important class in Okio, the cornerstone of the entire Okio library, and many of the optimization ideas are embodied in this class. Not much nonsense, let's look at the inheritance of this class first.

As you can see, this buffer is a synthesizer that implements the interface of Bufferedsink and Bufferedsource, that is, the ability to read data from it, and to write data to it, is beyond doubt. The advantages of Okio mentioned earlier, such as low CPU consumption and low frequency GC, are all done in this class. I'll tell you more in the later chapters.

ByteString

ByteString is a relatively independent class and can also be seen as a tool class. Its function we look at the method at a glance.

As you can see, there are summary functions for calculating MD5,SHA1, as well as uppercase and lowercase conversion functions, as well as 16 character conversion functions and so on. I'm not going to talk about this class, because it's very simple, but it's a few fields to mention.

  finalbyte[] data;  transient// Lazily computed.

Because this class is immutable (cannot modify its data after it has been created), it is based on byte[]. Also contains a string, although the delay is initialized, but also contains double the string data, its memory consumption is relatively large, it applies to not long strings, but also requires frequent encoding conversion, with space to change time, can reduce CPU consumption, Buit new string (byte[] Data) is still quite expensive.

ByteString also has a sub-class segmentedbytestring, followed by the introduction of buffer.

Okio

Okio is an ingress class that provides some conversion from JAVAAPI to Okioapi, which acts as an adapter (adapter). For example, create Sink/source from File/socket, create Source/sink from Inputstream/outputstream, and so on, so that we can use this set of APIs with Java.

Design principle of buffer

Next, let's introduce this buffer.

The implementation of buffer is achieved through a circular doubly linked list. Each linked list element is a segment.

Seqment
FinalClass Segment {/** the size of the data contained in each segment, fixed */  Static Final intSIZE =8192;/** The minimum number of bytes used for sharing, explained later .  Static Final intShare_minimum =1024x768;Final byte[] data;/** the location of the next read in the data array */  intPos/** the location of the next write in the data array */  intLimit/** data array is shared by other segsment * /  BooleanSharedwhether/** is the operator /  BooleanOwner/** Next segment in a linked or circularly-linked list. * /Segment Next;/** Previous Segment in a circularly-linked list. * /Segment prev;}

There are several interesting methods in segment.

Compact method
  /** * The tail and its predecessor may both is less than half * full.   This would copy data so, segments can be recycled. */   Public void Compact() {if(prev = = This)Throw NewIllegalStateException ();if(!prev.owner)return;//cannot compact:prev isn ' t writable.    intByteCount = Limit-pos;intAvailablebytecount = Size-prev.limit + (prev.shared?0: Prev.pos);if(ByteCount > Availablebytecount)return;//cannot compact:not enough writable space.WriteTo (prev, byteCount);    Pop (); Segmentpool.recycle ( This); }

When the segment and its own amount of data are less than half, the segement is compressed, its data is written to the previous segment, and then it is recycled.

Split

Split a segment data into two, note that there are trick. If there are two segment of the same byte than Share_minimum (1024), then the two segment will share a piece of data, thus eliminating the overhead of opening up memory and replicating memory, and achieving the purpose of high performance.

 PublicSegmentSplit(intByteCount) {if(ByteCount <=0|| ByteCount > Limit-pos)Throw NewIllegalArgumentException (); Segment prefix;//We have both competing performance goals:    //-Avoid copying data. We accomplish this by sharing segments.    //-Avoid short shared segments. These is bad for performance because they is readonly and    // may leads to long chains of the short segments.    //To balance these goals we have share segments when the copy would be large.    if(ByteCount >= Share_minimum) {prefix =NewSegment ( This); }Else{prefix = Segmentpool.take (); System.arraycopy (data, POS, Prefix.data,0, ByteCount);    } prefix.limit = Prefix.pos + byteCount;    pos + = ByteCount; Prev.push (prefix);returnPrefix }
Segmentpool

This is a recycling pool, the current design is capable of storing 64K of bytes, that is, 8 segment. In practical use, it is recommended to adjust it.

final class SegmentPool {  /** The maximum number of bytes to pool. */  // TODO: Is 64 KiB a good maximum size? Do we ever have that many idle segments?  staticfinallong641024// 64 KiB.  /** Singly-linked list of segments. */  static Segment next;  /** Total bytes in this pool. */  staticlong byteCount;    ...}

In this case, the entire buffer implementation principle is also in place.

The write operation of buffer, in fact, is a process of increasing segment, read operation, is constantly consuming the data in segment, if the data read, then use Segmentpool to recycle.
When replicating memory data, multiple segment share a copy of data[] using segment's sharing mechanism.

Buffer more logic is mainly to read the data across segment, need to put the front end of the previous segment and the rear of the front segment stitching together, so it seems that the amount of code is relatively small, but in fact the cost is very low.

Timeout mechanism

A class called timeout is defined in Okio, which is used primarily to determine whether the time exceeds the threshold and then throws an interrupt exception.

 public   void  throwifreached  () throws  IOException {if  (thread.interrupted ()) {throw  new  interruptedioexception ( "thread Interrupted" ); } if  (Hasdeadline && deadlinenanotime-system.nanotime () <= 0 ) {throw  new     Interruptedioexception ( "Deadline reached" ); }  } 

Interestingly, an asynchronous timeout class asynctimeout is defined. A background thread in which a watchdog is used. The asynctimeout itself is sorted by time-out in the form of an ordered list. In its head is a placeholder asynctime, which is used primarily to start the watchdog thread. This asynchronous timeout can be used mainly at the time of the time, you can immediately get notification, do not need to wait for a blocking method to return, only to know the timeout. With an asynchronous timeout, the timeout method makes a callback when a timeout occurs, and the TimedOut () method needs to be overloaded to handle the timeout event.

Summary

By learning the source code of Okio, we can understand common application optimization methods and technical details.

In-depth understanding of Okio's optimization ideas

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.