5
5
import java .io .ObjectOutputStream ;
6
6
import java .io .Serializable ;
7
7
import java .lang .ref .SoftReference ;
8
- import java .util .Arrays ;
9
8
import sun .misc .Unsafe ;
10
9
import java .lang .reflect .Field ;
11
10
11
+ /**
12
+ * A high-performance, immutable message container optimized for memory
13
+ * efficiency and fast access.
14
+ * This class uses direct memory operations via {@link sun.misc.Unsafe} for
15
+ * improved performance
16
+ * and implements custom serialization for better control over the serialization
17
+ * process.
18
+ *
19
+ * <p>
20
+ * The message contains:
21
+ * <ul>
22
+ * <li>A byte array containing the message data
23
+ * <li>A timestamp recording when the message was created
24
+ * <li>A message type identifier
25
+ * </ul>
26
+ *
27
+ * <p>
28
+ * This class implements optimizations including:
29
+ * <ul>
30
+ * <li>Direct memory access using Unsafe for field operations
31
+ * <li>Cached hash code using soft references to allow GC if memory is tight
32
+ * <li>Custom serialization implementation for performance
33
+ * </ul>
34
+ *
35
+ * @see MessageSerializer
36
+ */
37
+ @ SuppressWarnings ("deprecation" )
12
38
public class Message implements Serializable {
13
- private static final long serialVersionUID = 1L ;
14
-
15
- // Use SoftReference for the cached hash to allow GC if memory is tight
16
- private transient SoftReference <Integer > hashCache ;
17
-
18
- // Direct byte array reference for minimal overhead
19
- private final byte [] data ;
20
- private final long timestamp ;
21
- private final int messageType ;
22
-
23
- // Cache array length to avoid field access
24
- private final int length ;
25
-
26
- // Unsafe instance for direct memory operations
27
- private static final Unsafe unsafe ;
28
-
29
- // Field offsets for direct memory access
30
- private static final long dataOffset ;
31
- private static final long timestampOffset ;
32
- private static final long messageTypeOffset ;
33
-
34
- static {
35
- try {
36
- Field f = Unsafe .class .getDeclaredField ("theUnsafe" );
37
- f .setAccessible (true );
38
- unsafe = (Unsafe ) f .get (null );
39
-
40
- dataOffset = unsafe .objectFieldOffset (Message .class .getDeclaredField ("data" ));
41
- timestampOffset = unsafe .objectFieldOffset (Message .class .getDeclaredField ("timestamp" ));
42
- messageTypeOffset = unsafe .objectFieldOffset (Message .class .getDeclaredField ("messageType" ));
43
- } catch (Exception e ) {
44
- throw new Error (e );
45
- }
46
- }
47
-
48
- public Message (byte [] data , int messageType ) {
49
- // Avoid double array length access
50
- int dataLength = data .length ;
51
- this .data = new byte [dataLength ];
52
- // Direct memory copy instead of Arrays.copyOf
53
- unsafe .copyMemory (data , Unsafe .ARRAY_BYTE_BASE_OFFSET ,
54
- this .data , Unsafe .ARRAY_BYTE_BASE_OFFSET ,
55
- dataLength );
56
- this .length = dataLength ;
57
- this .timestamp = System .currentTimeMillis ();
58
- this .messageType = messageType ;
59
- }
60
-
61
- public byte [] getData () {
62
- byte [] copy = new byte [length ];
63
- // Direct memory copy for better performance
64
- unsafe .copyMemory (data , Unsafe .ARRAY_BYTE_BASE_OFFSET ,
65
- copy , Unsafe .ARRAY_BYTE_BASE_OFFSET ,
66
- length );
67
- return copy ;
68
- }
69
-
70
- public long getTimestamp () {
71
- // Direct memory access instead of field access
72
- return unsafe .getLong (this , timestampOffset );
73
- }
74
-
75
- public int getMessageType () {
76
- // Direct memory access instead of field access
77
- return unsafe .getInt (this , messageTypeOffset );
39
+ private static final long serialVersionUID = 1L ;
40
+
41
+ private transient SoftReference <Integer > hashCache ;
42
+ private final byte [] data ;
43
+ private final long timestamp ;
44
+ private final int messageType ;
45
+ private final int length ;
46
+
47
+ private static final Unsafe unsafe ;
48
+ @ SuppressWarnings ("unused" )
49
+ private static final long dataOffset ;
50
+ private static final long timestampOffset ;
51
+ private static final long messageTypeOffset ;
52
+
53
+ static {
54
+ try {
55
+ Field f = Unsafe .class .getDeclaredField ("theUnsafe" );
56
+ f .setAccessible (true );
57
+ unsafe = (Unsafe ) f .get (null );
58
+
59
+ dataOffset = unsafe .objectFieldOffset (Message .class .getDeclaredField ("data" ));
60
+ timestampOffset = unsafe .objectFieldOffset (Message .class .getDeclaredField ("timestamp" ));
61
+ messageTypeOffset = unsafe .objectFieldOffset (Message .class .getDeclaredField ("messageType" ));
62
+ } catch (Exception e ) {
63
+ throw new Error (e );
78
64
}
79
-
80
- @ Override
81
- public int hashCode () {
82
- Integer cachedHash = hashCache != null ? hashCache .get () : null ;
83
- if (cachedHash != null ) {
84
- return cachedHash ;
85
- }
86
-
87
- // FNV-1a hash algorithm - faster than Arrays.hashCode
88
- int hash = 0x811c9dc5 ;
89
- for (byte b : data ) {
90
- hash ^= b ;
91
- hash *= 0x01000193 ;
92
- }
93
- hash = hash * 31 + (int )(timestamp ^ (timestamp >>> 32 ));
94
- hash = hash * 31 + messageType ;
95
-
96
- hashCache = new SoftReference <>(hash );
97
- return hash ;
98
- }
99
-
100
- private void writeObject (ObjectOutputStream out ) throws IOException {
101
- // Direct field access for better performance
102
- out .writeLong (unsafe .getLong (this , timestampOffset ));
103
- out .writeInt (unsafe .getInt (this , messageTypeOffset ));
104
- out .writeInt (length );
105
- out .write (data , 0 , length );
65
+ }
66
+
67
+ /**
68
+ * Creates a new Message with the specified data and message type.
69
+ * The message's timestamp is automatically set to the current system time.
70
+ * A defensive copy of the input data is made to ensure immutability.
71
+ *
72
+ * @param data the byte array containing the message data
73
+ * @param messageType an integer identifying the type of message
74
+ * @throws NullPointerException if data is null
75
+ */
76
+ public Message (byte [] data , int messageType ) {
77
+ int dataLength = data .length ;
78
+ this .data = new byte [dataLength ];
79
+ unsafe .copyMemory (data , Unsafe .ARRAY_BYTE_BASE_OFFSET ,
80
+ this .data , Unsafe .ARRAY_BYTE_BASE_OFFSET ,
81
+ dataLength );
82
+ this .length = dataLength ;
83
+ this .timestamp = System .currentTimeMillis ();
84
+ this .messageType = messageType ;
85
+ }
86
+
87
+ /**
88
+ * Returns a copy of the message data.
89
+ * A new array is created and returned each time to preserve immutability.
90
+ *
91
+ * @return a copy of the message data as a byte array
92
+ */
93
+ public byte [] getData () {
94
+ byte [] copy = new byte [length ];
95
+ unsafe .copyMemory (data , Unsafe .ARRAY_BYTE_BASE_OFFSET ,
96
+ copy , Unsafe .ARRAY_BYTE_BASE_OFFSET ,
97
+ length );
98
+ return copy ;
99
+ }
100
+
101
+ /**
102
+ * Returns the timestamp when this message was created.
103
+ *
104
+ * @return the message creation timestamp as milliseconds since epoch
105
+ */
106
+ public long getTimestamp () {
107
+ return unsafe .getLong (this , timestampOffset );
108
+ }
109
+
110
+ /**
111
+ * Returns the message type identifier.
112
+ *
113
+ * @return the integer message type
114
+ */
115
+ public int getMessageType () {
116
+ return unsafe .getInt (this , messageTypeOffset );
117
+ }
118
+
119
+ /**
120
+ * Computes and caches the hash code for this message using the FNV-1a
121
+ * algorithm.
122
+ * The hash is computed based on the message data, timestamp, and message type.
123
+ * The computed hash is cached using a {@link SoftReference} to allow garbage
124
+ * collection
125
+ * if memory is tight.
126
+ *
127
+ * @return the hash code for this message
128
+ */
129
+ @ Override
130
+ public int hashCode () {
131
+ Integer cachedHash = hashCache != null ? hashCache .get () : null ;
132
+ if (cachedHash != null ) {
133
+ return cachedHash ;
106
134
}
107
135
108
- private void readObject (ObjectInputStream in ) throws IOException {
109
- throw new IOException ("Use MessageSerializer instead" );
136
+ int hash = 0x811c9dc5 ;
137
+ for (byte b : data ) {
138
+ hash ^= b ;
139
+ hash *= 0x01000193 ;
110
140
}
141
+ hash = hash * 31 + (int ) (timestamp ^ (timestamp >>> 32 ));
142
+ hash = hash * 31 + messageType ;
143
+
144
+ hashCache = new SoftReference <>(hash );
145
+ return hash ;
146
+ }
147
+
148
+ /**
149
+ * Custom serialization implementation for better performance.
150
+ * Writes the message fields directly to the output stream.
151
+ *
152
+ * @param out the output stream to write to
153
+ * @throws IOException if an I/O error occurs
154
+ */
155
+ private void writeObject (ObjectOutputStream out ) throws IOException {
156
+ out .writeLong (unsafe .getLong (this , timestampOffset ));
157
+ out .writeInt (unsafe .getInt (this , messageTypeOffset ));
158
+ out .writeInt (length );
159
+ out .write (data , 0 , length );
160
+ }
161
+
162
+ /**
163
+ * Disabled default deserialization.
164
+ * Use {@link MessageSerializer} instead for proper deserialization.
165
+ *
166
+ * @param in the input stream to read from
167
+ * @throws IOException always, to prevent default deserialization
168
+ */
169
+ private void readObject (ObjectInputStream in ) throws IOException {
170
+ throw new IOException ("Use MessageSerializer instead" );
171
+ }
111
172
}
0 commit comments