-
Notifications
You must be signed in to change notification settings - Fork 1
/
minimize-memory.uc.js
176 lines (152 loc) · 6.16 KB
/
minimize-memory.uc.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
// ==UserScript==
// @name Minimize Memory Usage
// @author jterror
// @homepageURL https://github.com/update692/firefox
// @version 1.0
// @include main
// @onlyonce
// ==/UserScript==
(function () {
console.log(">>>>: Minimize Memory Usage: START");
const threshold = 1000; // script is activated only when Firefox uses (megabytes) or more RAM
const minz_limit = 500; // minimize memory when relative RAM consumption exceeds (megabytes)
const poll_interval = 30000; // check RAM consumption value every (milliseconds)
const poll_number = 3; // how many RAM consumption values to aggregate for decision
// must be less than poll_interval
const cooldown_time = 5000; // give (milliseconds) for RAM level to stalilize after minimizing
const debug_beep = true; // sound beep when memory is cleared
const beep_time = 0.1; // beep duration (seconds)
const round_mb = 20; // round RAM values to (megabytes)
const STAT_NONE = 0;
const STAT_HIGH = 1;
const STAT_LOW = 2;
let current_level = threshold; // base for tracking rising memory
let lower_level = threshold; // base for tracking falling memory
const Mgr = Cc["@mozilla.org/memory-reporter-manager;1"].getService(Ci.nsIMemoryReporterManager);
let timer_poll; // persistent variable so nsITimer doesn't disappear
let timer_cooldown; // take time for RAM to stabilize after minimizing
function roundToNearest(number, increment) {
if (increment === 0) return number;
if (increment < 0) increment = Math.abs(increment);
return Math.round(number / increment) * increment;
}
function setTimeout(callback, ms, varname) {
setTimer(callback, ms, Ci.nsITimer.TYPE_ONE_SHOT, varname);
}
function setInterval(callback, ms, varname) {
setTimer(callback, ms, Ci.nsITimer.TYPE_REPEATING_SLACK, varname);
}
function setTimer(callback, ms, type, varname) {
eval(
`${varname} = Cc['@mozilla.org/timer;1'].createInstance(Ci.nsITimer);
${varname}.initWithCallback({notify: callback}, ms, type);`
);
}
// function clearTimer(timer) {
// timer.cancel();
// }
async function doMMU() {
if (debug_beep) doBeep();
Services.obs.notifyObservers(null, "child-mmu-request");
Mgr.minimizeMemoryUsage(() => {
console.log(`>>>>: Memory minimization completed (${new Date().toISOString()})`);
setTimeout(async () => {
const megabytes = await getRAM();
current_level = megabytes;
lower_level = megabytes;
console.log(`>>>>: level updated: ${megabytes}`);
}, cooldown_time, "timer_cooldown");
});
}
async function doBeep() {
const audioContext = new (window.AudioContext || window.webkitAudioContext)();
const oscillator = audioContext.createOscillator();
const gainNode = audioContext.createGain();
gainNode.gain.value = 0.5;
oscillator.connect(gainNode);
gainNode.connect(audioContext.destination);
oscillator.type = 'sine';
oscillator.frequency.value = 440; // A4 note
oscillator.start();
oscillator.stop(audioContext.currentTime + beep_time);
}
async function getRAM() {
const info = await ChromeUtils.requestProcInfo();
let bytes = info.memory;
for (let child of info.children) bytes += child.memory;
return roundToNearest(Math.round(bytes / 1048576), round_mb);
}
function checkStat(buffer, current, limit) {
const values = buffer.getValues();
let ret = STAT_NONE;
if (values.every(value => value >= current + limit))
ret = STAT_HIGH;
else if (values.every(value => value < current))
ret = STAT_LOW;
return ret;
}
class CircularBuffer {
constructor(size) {
this.size = size;
this.buffer = new Array(size);
this.clear();
}
clear() {
this.count = 0;
this.index = 0;
}
add(value) {
if (this.count < this.size) {
this.buffer[this.count] = value;
this.count++;
} else {
this.buffer[this.index] = value;
this.index = (this.index + 1) % this.size;
}
}
getValues() {
let values = [];
for (let i = 0; i < this.count; i++) {
values.push(this.buffer[(this.index + i) % this.size]);
}
return values;
}
isFull() {
return this.count >= this.size;
}
}
const statbuf = new CircularBuffer(poll_number);
function doReset() {
current_level = threshold;
lower_level = threshold;
statbuf.clear();
}
setInterval(async () => {
const megabytes = await getRAM();
let log_msg = `>>>>: ${megabytes} MB, level: ${current_level}, level-down: ${lower_level}, threshold: ${threshold}, limit: ${minz_limit}`;
if (megabytes >= threshold) {
statbuf.add(megabytes);
if (statbuf.isFull()) {
let result = checkStat(statbuf, current_level, minz_limit);
if (result === STAT_HIGH) {
log_msg += " : high>>";
doMMU();
} else if (result === STAT_LOW) {
log_msg += " : low<<";
current_level = megabytes;
if (current_level <= lower_level - Math.round(minz_limit / 2)) {
doMMU();
}
} else {
log_msg += " : none";
}
} else {
log_msg += " : filling^^";
}
} else {
log_msg += " : reset--";
doReset();
}
console.log(log_msg);
}, poll_interval, "timer_poll");
})();