Boost Java Application Performance: Buffering, Caching, and Object Reuse Techniques
This article explains how to improve the throughput and response time of Java systems by using buffers, caches, object‑reuse pools, and time‑space trade‑off algorithms, providing concrete code examples and performance measurements for each technique.
High‑Throughput Systems
Like a project schedule, a system has a critical path composed of CPU work, I/O, and external service latency that determines overall response time, which directly shapes user experience.
Buffer (Buffering)
A buffer is a dedicated memory area that smooths performance differences between producer and consumer components, similar to a funnel, allowing the upper layer to continue processing without waiting for the lower layer to finish.
Using BufferedWriter correctly requires a buffer that is neither too small (ineffective) nor too large (wastes memory and adds GC pressure). Example before adding a buffer:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import javax.swing.JApplet;
public class NoBufferMovingCircle extends JApplet implements Runnable{
Image screenImage = null;
Thread thread;
int x = 5;
int move = 1;
public void init(){
screenImage = createImage(230,160);
}
public void start(){
if(thread == null){
thread = new Thread(this);
thread.start();
}
}
@Override
public void run(){
try{
while(true){
x+=move;
if((x>105)||(x<5)) move*=-1;
repaint();
Thread.sleep(10);
}
}catch(Exception e){}
}
public void drawCircle(Graphics gc){
Graphics2D g = (Graphics2D) gc;
g.setColor(Color.GREEN);
g.fillRect(0,0,200,100);
g.setColor(Color.red);
g.fillOval(x,5,90,90);
}
public void paint(Graphics g){
g.setColor(Color.white);
g.fillRect(0,0,200,100);
drawCircle(g);
}
}After adding a double buffer the drawing flicker disappears:
public class BufferMovingCircle extends NoBufferMovingCircle{
Graphics doubleBuffer = null; // buffer
public void init(){
super.init();
doubleBuffer = screenImage.getGraphics();
}
public void paint(Graphics g){
doubleBuffer.setColor(Color.white);
doubleBuffer.fillRect(0,0,200,100);
drawCircle(doubleBuffer);
g.drawImage(screenImage,0,0,this);
}
}Using Buffer for I/O
Java I/O can be performed via InputStream/OutputStream or Reader/Writer. Wrapping these streams with buffered variants ( BufferedInputStream, BufferedOutputStream, BufferedReader, BufferedWriter) dramatically reduces file read/write time.
Performance test (writing/reading 10,000 lines):
public static void streamMethod() throws IOException{ ... }
public static void bufferMethod() throws IOException{ ... }
public static void main(String[] args){
StreamVSBuffer.streamMethod();
StreamVSBuffer.bufferMethod();
}Typical output shows buffered version ~30 ms versus ~900 ms for the unbuffered version.
Cache
A cache stores computed results in memory (and optionally on disk) to avoid repeated expensive operations. Java cache frameworks include EHCache, OSCache, and JBossCache. Example using EHCache:
import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Element;
public class EHCacheDemo{
public static void main(String[] args){
CacheManager manager = new CacheManager("ehcache.xml");
Cache cache = manager.getCache(manager.getCacheNames()[0]);
cache.put(new Element("key1","values1"));
Element element = cache.get("key1");
System.out.println(element.getValue());
manager.shutdown();
}
}Object Reuse (Object Pools)
Frequently used objects can be kept in a pool to avoid costly creation. Common pools are thread pools and database connection pools (e.g., C3P0). Example C3P0 configuration for Tomcat:
<Resource name="jdbc/dbsource" type="com.mchange.v2.c3p0.ComboPooledDataSource"
maxPoolSize="50" minPoolSize="5" acquireIncrement="2" initialPoolSize="10"
idleConnectionTestPeriod="10" acquireRetryAttempts="30" breakAfterAcquireFailure="true"/>Spring XML configuration can also define the same pool.
Computation Trade‑offs (Time‑Space Conversion)
Sometimes CPU cycles are exchanged for memory savings. Example of swapping two variables without a temporary variable:
a = a + b;
b = a - b;
a = a - b;Unsigned byte handling in Java (which lacks native unsigned types) using bit‑masking:
public short getValue(byte i){ return (short)(i & 0xff); }
public byte toUnsignedByte(short i){ return (byte)(i & 0xff); }A space‑to‑time sorting algorithm that uses an auxiliary array indexed by value to achieve linear‑time sorting at the cost of extra memory:
public static void spaceTotime(int[] array){
int max = array[0];
for(int v: array) if(v>max) max=v;
int[] temp = new int[max+1];
for(int v: array) temp[v] = v;
int idx = 0;
for(int i=0;i<temp.length;i++) if(temp[i]>0) array[idx++] = temp[i];
}Conclusion
Optimizing high‑throughput Java systems involves buffering, caching, object‑reuse pools, and appropriate time‑space trade‑offs; each technique should be evaluated against real‑world workloads to achieve the best performance gains.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
21CTO
21CTO (21CTO.com) offers developers community, training, and services, making it your go‑to learning and service platform.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
