This is a general question about writing java classes and how they're instantiated.
public class transaction
{
int amount;
char ref;
}
If a class is written like this then it can be used like a struct. Then when data comes over the network as a byte[] in a datagram it's converted into a transaction object. One place to do this is in a separate class, say like this:
public class doStuff
{
static transaction t; // the int and the char are alloc'd onto the DataSegment
public static transaction fromByte(byte[] buf)
{
t = new transaction(); // make sure t is null to begin with (on the heap)
t.ref = ''; // initialise char (to avoid null bytes)
t.amount = ByteBuffer.wrap(buf).getInt();
t.ref = ByteBuffer.wrap(buf).getChar();
return t;
}
}
Then another class calls doStuff like so:
import doStuff;
class otherClass extends Thread
{
static transaction x = new transaction();
... in the run method
x = doStuff.fromByte(buf);
...
}
But now I want to keep the class data and methods together in one place (supposedly like it should be?) so instead of having the fromByte(byte[] buf) method in the doStuff class it's moved to the transaction class. So the transaction class now looks like this:
public class transaction
{
int amount;
char ref;
static transaction t;
public static transaction fromByte(byte[] buf)
{
t = new transaction(); // make sure t is null to begin with
t.ref = ''; // initialise char (to avoid null bytes)
t.amount = ByteBuffer.wrap(buf).getInt();
t.ref = ByteBuffer.wrap(buf).getChar();
return t;
}
}
Then in the otherClass I use:
import transaction;
class otherClass extends Thread
{
static transaction x = new transaction();
... in the run method
x = fromByte(buf);
...
}
and on the surface it all has the same effect as before.
My question is: Having added the operation fromByte(byte[] buf) on the transaction data (amount and ref) into the transaction class then the overhead to instantiate a transaction object changes. If there were hundreds of transactions per second coming from the network then adding the fromByte(byte[] buf) method to the transaction class means that when it's instantiated in the doStuff class there's going to be more overhead used than before. In other words instead of simply generating an int and a char (as a static variable on the data segment) each time the doStuff class generates foo then it's generated on the heap (i think, rather than the data segment) and further the fromByte(buf) method is pushed to the stack AND then the transaction class calls itself recursively on the data segment again (a static variable)...
well it seems a bit of a mess. is there a better way to put the data and method in the same class and retain maximum speed? can it get over the recursive variable call (the fromByte method returns a transaction object and that's ok in the 'int/char' form) any comment? :-)
This will read from the start of the buf twice.
t.amount = ByteBuffer.wrap(buf).getInt();
t.ref = ByteBuffer.wrap(buf).getChar();
I suspect you meant
ByteBuffer bb = ByteBuffer.wrap(buf);
t.amount = bb.getInt();
t.ref = bb.getChar();
Having added the operation fromByte(byte[] buf) on the transaction data (amount and ref) into the transaction class then the overhead to instantiate a transaction object changes.
Creating a byte[] and ByteBuffer each time is an overhead as well.
In other words instead of simply generating an int and a char (as a static variable on the data segment) each time the doStuff class generates foo then it's generated on the heap (i think, rather than the data segment) and further the fromByte(buf) method is pushed to the stack AND then the transaction class calls itself recursively on the data segment again (a static variable)...
As mentioned, creating the transaction obejct is likely to be a small portion of your overhead. E.g. reading from a Socket will take 100x longer at best.
well it seems a bit of a mess. is there a better way to put the data and method in the same class and retain maximum speed?
Don't use byte[], don't create a new ByteBuffer each time and don't create a new transaction object each time. You can create these objects in advance or not at all and re-use them.
For example consider creating one direct ByteBuffer when the connection is establish (or recycle one) and decode the message and call a listener like
interface TransactionListener {
public void onTransaction(int num, char status);
}
This way no object is created, on the stack or otherwise.
In this situation, your performance bottleneck will be reading from a Socket, depending on what you do with this information. ;)
In terms of performance, the only difference between the two approaches is the cost of a call to the static method. This is a really trivial cost and is further reduced by JIT compilers.
In terms of encapsulation, the question is whether you want to put knowledge of the transaction class in the doStuff class or whether you want to put knowledge of the byte stream structure into the transaction class. There's an argument to be made either way.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With