musubi-sdd 5.6.3 → 5.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -0,0 +1,557 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Memory Optimizer
|
|
3
|
+
*
|
|
4
|
+
* Phase 6 P1: Memory Optimization for enterprise-grade performance
|
|
5
|
+
*
|
|
6
|
+
* Features:
|
|
7
|
+
* - Object pooling for frequently created objects
|
|
8
|
+
* - Weak reference caching for large objects
|
|
9
|
+
* - Memory pressure monitoring
|
|
10
|
+
* - Automatic garbage collection hints
|
|
11
|
+
*
|
|
12
|
+
* @module src/performance/memory-optimizer
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
'use strict';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Memory pressure levels
|
|
19
|
+
*/
|
|
20
|
+
const MemoryPressure = {
|
|
21
|
+
LOW: 'low', // < 50% heap used
|
|
22
|
+
MODERATE: 'moderate', // 50-75% heap used
|
|
23
|
+
HIGH: 'high', // 75-90% heap used
|
|
24
|
+
CRITICAL: 'critical', // > 90% heap used
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Object Pool for reusable objects
|
|
29
|
+
* Reduces GC pressure by recycling objects
|
|
30
|
+
*/
|
|
31
|
+
class ObjectPool {
|
|
32
|
+
/**
|
|
33
|
+
* @param {Function} factory - Factory function to create new objects
|
|
34
|
+
* @param {Object} options
|
|
35
|
+
* @param {number} options.initialSize - Initial pool size
|
|
36
|
+
* @param {number} options.maxSize - Maximum pool size
|
|
37
|
+
* @param {Function} options.reset - Reset function for recycled objects
|
|
38
|
+
*/
|
|
39
|
+
constructor(factory, options = {}) {
|
|
40
|
+
this.factory = factory;
|
|
41
|
+
this.maxSize = options.maxSize || 100;
|
|
42
|
+
this.reset = options.reset || (obj => obj);
|
|
43
|
+
this.pool = [];
|
|
44
|
+
this.created = 0;
|
|
45
|
+
this.recycled = 0;
|
|
46
|
+
this.borrowed = 0;
|
|
47
|
+
|
|
48
|
+
// Pre-populate pool
|
|
49
|
+
const initialSize = options.initialSize || 10;
|
|
50
|
+
for (let i = 0; i < Math.min(initialSize, this.maxSize); i++) {
|
|
51
|
+
this.pool.push(this.factory());
|
|
52
|
+
this.created++;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Acquire an object from the pool
|
|
58
|
+
* @returns {*} Object from pool or newly created
|
|
59
|
+
*/
|
|
60
|
+
acquire() {
|
|
61
|
+
this.borrowed++;
|
|
62
|
+
if (this.pool.length > 0) {
|
|
63
|
+
this.recycled++;
|
|
64
|
+
return this.pool.pop();
|
|
65
|
+
}
|
|
66
|
+
this.created++;
|
|
67
|
+
return this.factory();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Release an object back to the pool
|
|
72
|
+
* @param {*} obj - Object to release
|
|
73
|
+
*/
|
|
74
|
+
release(obj) {
|
|
75
|
+
if (this.pool.length < this.maxSize) {
|
|
76
|
+
this.reset(obj);
|
|
77
|
+
this.pool.push(obj);
|
|
78
|
+
}
|
|
79
|
+
// If pool is full, let GC handle it
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Get pool statistics
|
|
84
|
+
* @returns {Object}
|
|
85
|
+
*/
|
|
86
|
+
getStats() {
|
|
87
|
+
return {
|
|
88
|
+
poolSize: this.pool.length,
|
|
89
|
+
maxSize: this.maxSize,
|
|
90
|
+
created: this.created,
|
|
91
|
+
recycled: this.recycled,
|
|
92
|
+
borrowed: this.borrowed,
|
|
93
|
+
recycleRate: this.borrowed > 0 ? this.recycled / this.borrowed : 0,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Clear the pool
|
|
99
|
+
*/
|
|
100
|
+
clear() {
|
|
101
|
+
this.pool = [];
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Weak Cache for large objects
|
|
107
|
+
* Allows GC to collect objects when memory is needed
|
|
108
|
+
*/
|
|
109
|
+
class WeakCache {
|
|
110
|
+
constructor() {
|
|
111
|
+
this.cache = new Map();
|
|
112
|
+
this.hits = 0;
|
|
113
|
+
this.misses = 0;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Set a value with weak reference
|
|
118
|
+
* @param {string} key
|
|
119
|
+
* @param {Object} value - Must be an object (for WeakRef)
|
|
120
|
+
*/
|
|
121
|
+
set(key, value) {
|
|
122
|
+
if (typeof value !== 'object' || value === null) {
|
|
123
|
+
throw new Error('WeakCache only supports object values');
|
|
124
|
+
}
|
|
125
|
+
this.cache.set(key, new WeakRef(value));
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Get a value (may return undefined if GC collected it)
|
|
130
|
+
* @param {string} key
|
|
131
|
+
* @returns {Object|undefined}
|
|
132
|
+
*/
|
|
133
|
+
get(key) {
|
|
134
|
+
const ref = this.cache.get(key);
|
|
135
|
+
if (!ref) {
|
|
136
|
+
this.misses++;
|
|
137
|
+
return undefined;
|
|
138
|
+
}
|
|
139
|
+
const value = ref.deref();
|
|
140
|
+
if (value === undefined) {
|
|
141
|
+
this.cache.delete(key);
|
|
142
|
+
this.misses++;
|
|
143
|
+
return undefined;
|
|
144
|
+
}
|
|
145
|
+
this.hits++;
|
|
146
|
+
return value;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Check if key exists and is still valid
|
|
151
|
+
* @param {string} key
|
|
152
|
+
* @returns {boolean}
|
|
153
|
+
*/
|
|
154
|
+
has(key) {
|
|
155
|
+
const ref = this.cache.get(key);
|
|
156
|
+
if (!ref) return false;
|
|
157
|
+
const value = ref.deref();
|
|
158
|
+
if (value === undefined) {
|
|
159
|
+
this.cache.delete(key);
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
return true;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Delete a key
|
|
167
|
+
* @param {string} key
|
|
168
|
+
*/
|
|
169
|
+
delete(key) {
|
|
170
|
+
this.cache.delete(key);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Clean up dead references
|
|
175
|
+
* @returns {number} Number of cleaned entries
|
|
176
|
+
*/
|
|
177
|
+
cleanup() {
|
|
178
|
+
let cleaned = 0;
|
|
179
|
+
for (const [key, ref] of this.cache) {
|
|
180
|
+
if (ref.deref() === undefined) {
|
|
181
|
+
this.cache.delete(key);
|
|
182
|
+
cleaned++;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return cleaned;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Get cache statistics
|
|
190
|
+
* @returns {Object}
|
|
191
|
+
*/
|
|
192
|
+
getStats() {
|
|
193
|
+
return {
|
|
194
|
+
size: this.cache.size,
|
|
195
|
+
hits: this.hits,
|
|
196
|
+
misses: this.misses,
|
|
197
|
+
hitRate: this.hits + this.misses > 0 ? this.hits / (this.hits + this.misses) : 0,
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Clear the cache
|
|
203
|
+
*/
|
|
204
|
+
clear() {
|
|
205
|
+
this.cache.clear();
|
|
206
|
+
this.hits = 0;
|
|
207
|
+
this.misses = 0;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Memory Monitor for tracking heap usage
|
|
213
|
+
*/
|
|
214
|
+
class MemoryMonitor {
|
|
215
|
+
constructor(options = {}) {
|
|
216
|
+
this.checkInterval = options.checkInterval || 30000; // 30 seconds
|
|
217
|
+
this.thresholds = {
|
|
218
|
+
moderate: options.moderateThreshold || 0.5,
|
|
219
|
+
high: options.highThreshold || 0.75,
|
|
220
|
+
critical: options.criticalThreshold || 0.9,
|
|
221
|
+
};
|
|
222
|
+
this.history = [];
|
|
223
|
+
this.maxHistory = options.maxHistory || 100;
|
|
224
|
+
this.listeners = new Map();
|
|
225
|
+
this.intervalId = null;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Get current memory usage
|
|
230
|
+
* @returns {Object}
|
|
231
|
+
*/
|
|
232
|
+
getMemoryUsage() {
|
|
233
|
+
const usage = process.memoryUsage();
|
|
234
|
+
const heapUsed = usage.heapUsed;
|
|
235
|
+
const heapTotal = usage.heapTotal;
|
|
236
|
+
const heapRatio = heapTotal > 0 ? heapUsed / heapTotal : 0;
|
|
237
|
+
|
|
238
|
+
return {
|
|
239
|
+
heapUsed,
|
|
240
|
+
heapTotal,
|
|
241
|
+
heapRatio,
|
|
242
|
+
external: usage.external,
|
|
243
|
+
rss: usage.rss,
|
|
244
|
+
arrayBuffers: usage.arrayBuffers || 0,
|
|
245
|
+
timestamp: Date.now(),
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Get current pressure level
|
|
251
|
+
* @returns {string}
|
|
252
|
+
*/
|
|
253
|
+
getPressureLevel() {
|
|
254
|
+
const { heapRatio } = this.getMemoryUsage();
|
|
255
|
+
|
|
256
|
+
if (heapRatio >= this.thresholds.critical) return MemoryPressure.CRITICAL;
|
|
257
|
+
if (heapRatio >= this.thresholds.high) return MemoryPressure.HIGH;
|
|
258
|
+
if (heapRatio >= this.thresholds.moderate) return MemoryPressure.MODERATE;
|
|
259
|
+
return MemoryPressure.LOW;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Add a pressure level listener
|
|
264
|
+
* @param {string} level - Pressure level to listen for
|
|
265
|
+
* @param {Function} callback - Callback function
|
|
266
|
+
*/
|
|
267
|
+
onPressure(level, callback) {
|
|
268
|
+
if (!this.listeners.has(level)) {
|
|
269
|
+
this.listeners.set(level, []);
|
|
270
|
+
}
|
|
271
|
+
this.listeners.get(level).push(callback);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Start monitoring
|
|
276
|
+
*/
|
|
277
|
+
start() {
|
|
278
|
+
if (this.intervalId) return;
|
|
279
|
+
|
|
280
|
+
this.intervalId = setInterval(() => {
|
|
281
|
+
const usage = this.getMemoryUsage();
|
|
282
|
+
this.history.push(usage);
|
|
283
|
+
|
|
284
|
+
if (this.history.length > this.maxHistory) {
|
|
285
|
+
this.history.shift();
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
const pressure = this.getPressureLevel();
|
|
289
|
+
const callbacks = this.listeners.get(pressure) || [];
|
|
290
|
+
for (const callback of callbacks) {
|
|
291
|
+
try {
|
|
292
|
+
callback(usage, pressure);
|
|
293
|
+
} catch (_e) {
|
|
294
|
+
// Ignore callback errors
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}, this.checkInterval);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Stop monitoring
|
|
302
|
+
*/
|
|
303
|
+
stop() {
|
|
304
|
+
if (this.intervalId) {
|
|
305
|
+
clearInterval(this.intervalId);
|
|
306
|
+
this.intervalId = null;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Get memory statistics
|
|
312
|
+
* @returns {Object}
|
|
313
|
+
*/
|
|
314
|
+
getStats() {
|
|
315
|
+
if (this.history.length === 0) {
|
|
316
|
+
return { current: this.getMemoryUsage(), history: [], trend: 'stable' };
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
const current = this.getMemoryUsage();
|
|
320
|
+
const avgHeapRatio =
|
|
321
|
+
this.history.reduce((sum, h) => sum + h.heapRatio, 0) / this.history.length;
|
|
322
|
+
const recentAvg =
|
|
323
|
+
this.history.slice(-10).reduce((sum, h) => sum + h.heapRatio, 0) /
|
|
324
|
+
Math.min(10, this.history.length);
|
|
325
|
+
|
|
326
|
+
let trend = 'stable';
|
|
327
|
+
if (recentAvg > avgHeapRatio * 1.1) trend = 'increasing';
|
|
328
|
+
if (recentAvg < avgHeapRatio * 0.9) trend = 'decreasing';
|
|
329
|
+
|
|
330
|
+
return {
|
|
331
|
+
current,
|
|
332
|
+
pressure: this.getPressureLevel(),
|
|
333
|
+
avgHeapRatio,
|
|
334
|
+
recentAvgHeapRatio: recentAvg,
|
|
335
|
+
trend,
|
|
336
|
+
historyLength: this.history.length,
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Suggest garbage collection
|
|
342
|
+
* @returns {boolean} Whether GC was suggested
|
|
343
|
+
*/
|
|
344
|
+
suggestGC() {
|
|
345
|
+
const pressure = this.getPressureLevel();
|
|
346
|
+
if (pressure === MemoryPressure.HIGH || pressure === MemoryPressure.CRITICAL) {
|
|
347
|
+
if (global.gc) {
|
|
348
|
+
global.gc();
|
|
349
|
+
return true;
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
return false;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Streaming Buffer for memory-efficient large data processing
|
|
358
|
+
*/
|
|
359
|
+
class StreamingBuffer {
|
|
360
|
+
/**
|
|
361
|
+
* @param {Object} options
|
|
362
|
+
* @param {number} options.chunkSize - Size of each chunk
|
|
363
|
+
* @param {number} options.maxBuffered - Maximum buffered chunks
|
|
364
|
+
*/
|
|
365
|
+
constructor(options = {}) {
|
|
366
|
+
this.chunkSize = options.chunkSize || 1024 * 64; // 64KB
|
|
367
|
+
this.maxBuffered = options.maxBuffered || 10;
|
|
368
|
+
this.chunks = [];
|
|
369
|
+
this.totalProcessed = 0;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Add data to buffer
|
|
374
|
+
* @param {*} data
|
|
375
|
+
* @returns {boolean} Whether buffer is full
|
|
376
|
+
*/
|
|
377
|
+
push(data) {
|
|
378
|
+
this.chunks.push(data);
|
|
379
|
+
return this.chunks.length >= this.maxBuffered;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Get and clear buffered chunks
|
|
384
|
+
* @returns {Array}
|
|
385
|
+
*/
|
|
386
|
+
flush() {
|
|
387
|
+
const data = this.chunks;
|
|
388
|
+
this.totalProcessed += data.length;
|
|
389
|
+
this.chunks = [];
|
|
390
|
+
return data;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* Check if buffer has data
|
|
395
|
+
* @returns {boolean}
|
|
396
|
+
*/
|
|
397
|
+
hasData() {
|
|
398
|
+
return this.chunks.length > 0;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* Get buffer statistics
|
|
403
|
+
* @returns {Object}
|
|
404
|
+
*/
|
|
405
|
+
getStats() {
|
|
406
|
+
return {
|
|
407
|
+
buffered: this.chunks.length,
|
|
408
|
+
maxBuffered: this.maxBuffered,
|
|
409
|
+
totalProcessed: this.totalProcessed,
|
|
410
|
+
};
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* Clear the buffer
|
|
415
|
+
*/
|
|
416
|
+
clear() {
|
|
417
|
+
this.chunks = [];
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* Memory Optimizer - Main coordinator
|
|
423
|
+
*/
|
|
424
|
+
class MemoryOptimizer {
|
|
425
|
+
constructor(options = {}) {
|
|
426
|
+
this.monitor = new MemoryMonitor(options.monitor || {});
|
|
427
|
+
this.pools = new Map();
|
|
428
|
+
this.weakCaches = new Map();
|
|
429
|
+
this.autoGC = options.autoGC !== false;
|
|
430
|
+
|
|
431
|
+
if (this.autoGC) {
|
|
432
|
+
this.monitor.onPressure(MemoryPressure.HIGH, () => this.onHighPressure());
|
|
433
|
+
this.monitor.onPressure(MemoryPressure.CRITICAL, () => this.onCriticalPressure());
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
/**
|
|
438
|
+
* Create or get an object pool
|
|
439
|
+
* @param {string} name - Pool name
|
|
440
|
+
* @param {Function} factory - Object factory
|
|
441
|
+
* @param {Object} options - Pool options
|
|
442
|
+
* @returns {ObjectPool}
|
|
443
|
+
*/
|
|
444
|
+
createPool(name, factory, options = {}) {
|
|
445
|
+
if (!this.pools.has(name)) {
|
|
446
|
+
this.pools.set(name, new ObjectPool(factory, options));
|
|
447
|
+
}
|
|
448
|
+
return this.pools.get(name);
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
/**
|
|
452
|
+
* Get an existing pool
|
|
453
|
+
* @param {string} name
|
|
454
|
+
* @returns {ObjectPool|undefined}
|
|
455
|
+
*/
|
|
456
|
+
getPool(name) {
|
|
457
|
+
return this.pools.get(name);
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
/**
|
|
461
|
+
* Create or get a weak cache
|
|
462
|
+
* @param {string} name - Cache name
|
|
463
|
+
* @returns {WeakCache}
|
|
464
|
+
*/
|
|
465
|
+
createWeakCache(name) {
|
|
466
|
+
if (!this.weakCaches.has(name)) {
|
|
467
|
+
this.weakCaches.set(name, new WeakCache());
|
|
468
|
+
}
|
|
469
|
+
return this.weakCaches.get(name);
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
/**
|
|
473
|
+
* Get an existing weak cache
|
|
474
|
+
* @param {string} name
|
|
475
|
+
* @returns {WeakCache|undefined}
|
|
476
|
+
*/
|
|
477
|
+
getWeakCache(name) {
|
|
478
|
+
return this.weakCaches.get(name);
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
/**
|
|
482
|
+
* Handle high memory pressure
|
|
483
|
+
*/
|
|
484
|
+
onHighPressure() {
|
|
485
|
+
// Clean up weak caches
|
|
486
|
+
for (const cache of this.weakCaches.values()) {
|
|
487
|
+
cache.cleanup();
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
/**
|
|
492
|
+
* Handle critical memory pressure
|
|
493
|
+
*/
|
|
494
|
+
onCriticalPressure() {
|
|
495
|
+
// Clear all pools
|
|
496
|
+
for (const pool of this.pools.values()) {
|
|
497
|
+
pool.clear();
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
// Clear all weak caches
|
|
501
|
+
for (const cache of this.weakCaches.values()) {
|
|
502
|
+
cache.clear();
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
// Suggest GC
|
|
506
|
+
this.monitor.suggestGC();
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
/**
|
|
510
|
+
* Start memory monitoring
|
|
511
|
+
*/
|
|
512
|
+
start() {
|
|
513
|
+
this.monitor.start();
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
/**
|
|
517
|
+
* Stop memory monitoring
|
|
518
|
+
*/
|
|
519
|
+
stop() {
|
|
520
|
+
this.monitor.stop();
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
/**
|
|
524
|
+
* Get comprehensive statistics
|
|
525
|
+
* @returns {Object}
|
|
526
|
+
*/
|
|
527
|
+
getStats() {
|
|
528
|
+
const poolStats = {};
|
|
529
|
+
for (const [name, pool] of this.pools) {
|
|
530
|
+
poolStats[name] = pool.getStats();
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
const cacheStats = {};
|
|
534
|
+
for (const [name, cache] of this.weakCaches) {
|
|
535
|
+
cacheStats[name] = cache.getStats();
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
return {
|
|
539
|
+
memory: this.monitor.getStats(),
|
|
540
|
+
pools: poolStats,
|
|
541
|
+
weakCaches: cacheStats,
|
|
542
|
+
};
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
// Default instance
|
|
547
|
+
const defaultOptimizer = new MemoryOptimizer();
|
|
548
|
+
|
|
549
|
+
module.exports = {
|
|
550
|
+
MemoryPressure,
|
|
551
|
+
ObjectPool,
|
|
552
|
+
WeakCache,
|
|
553
|
+
MemoryMonitor,
|
|
554
|
+
StreamingBuffer,
|
|
555
|
+
MemoryOptimizer,
|
|
556
|
+
defaultOptimizer,
|
|
557
|
+
};
|