Fix #42839. avoid ui freeze when file is larger than heap size limit.

This commit is contained in:
Peng Lyu 2018-02-05 17:22:26 -08:00
parent 9f5a4a2102
commit 50a1deb9f6
8 changed files with 64 additions and 11 deletions

View file

@ -162,6 +162,21 @@ function touch(file) {
});
}
function resolveJSFlags() {
let jsFlags = [];
if (args['js-flags']) {
jsFlags.push(args['js-flag']);
}
if (args['max_old_space_size'] && !/max_old_space_size=(\d+)/g.exec(args['js-flags'])) {
jsFlags.push(`--max_old_space_size=${args['max_old_space_size']}`);
}
if (jsFlags.length > 0) {
return jsFlags.join(' ');
} else {
return null;
}
}
// Language tags are case insensitve however an amd loader is case sensitive
// To make this work on case preserving & insensitive FS we do the following:
// the language bundles have lower case language tags and we always lower case
@ -441,7 +456,8 @@ let nodeCachedDataDir = getNodeCachedDataDir().then(function (value) {
// tell v8 to not be lazy when parsing JavaScript. Generally this makes startup slower
// but because we generate cached data it makes subsequent startups much faster
app.commandLine.appendSwitch('--js-flags', '--nolazy');
let existingJSFlags = resolveJSFlags();
app.commandLine.appendSwitch('--js-flags', existingJSFlags ? existingJSFlags + ' --nolazy' : '--nolazy');
}
return value;
});
@ -454,6 +470,11 @@ userDefinedLocale.then((locale) => {
}
});
let jsFlags = resolveJSFlags();
if (jsFlags) {
app.commandLine.appendSwitch('--js-flags', jsFlags);
}
// Load our code once ready
app.once('ready', function () {
perf.mark('main:appReady');

View file

@ -306,6 +306,13 @@ export async function main(argv: string[]): TPromise<any> {
});
}
if (args['js-flags']) {
const match = /max_old_space_size=(\d+)/g.exec(args['js-flags']);
if (match && !args['max_old_space_size']) {
argv.push(`--max_old_space_size=${match[1]}`);
}
}
const options = {
detached: true,
env

View file

@ -53,6 +53,7 @@ export interface ParsedArgs {
'disable-updates'?: string;
'disable-crash-reporter'?: string;
'skip-add-to-recently-opened'?: boolean;
'max_old_space_size'?: number;
'file-write'?: boolean;
'file-chmod'?: boolean;
'upload-logs'?: string;

View file

@ -167,6 +167,7 @@ const troubleshootingHelp: { [name: string]: string; } = {
'--inspect-brk-extensions': localize('inspect-brk-extensions', "Allow debugging and profiling of extensions with the extension host being paused after start. Check the developer tools for the connection uri."),
'--disable-gpu': localize('disableGPU', "Disable GPU hardware acceleration."),
'--upload-logs': localize('uploadLogs', "Uploads logs from current session to a secure endpoint."),
'--max_old_space_size': localize('maxOldSpaceSize', "Max size of the old space (in Mbytes).")
};
export function formatOptions(options: { [name: string]: string; }, columns: number): string {

View file

@ -604,7 +604,8 @@ export enum FileOperationResult {
FILE_READ_ONLY,
FILE_PERMISSION_DENIED,
FILE_TOO_LARGE,
FILE_INVALID_PATH
FILE_INVALID_PATH,
FILE_EXCEED_HEAP
}
export const AutoSaveConfiguration = {

View file

@ -7,4 +7,9 @@
const WIN32_MAX_FILE_SIZE = 300 * 1024 * 1024; // 300 MB
const GENERAL_MAX_FILE_SIZE = 16 * 1024 * 1024 * 1024; // 16 GB
export const MAX_FILE_SIZE = process.arch === 'ia32' ? WIN32_MAX_FILE_SIZE : GENERAL_MAX_FILE_SIZE;
// See https://github.com/v8/v8/blob/5918a23a3d571b9625e5cce246bdd5b46ff7cd8b/src/heap/heap.cc#L149
const WIN32_MAX_HEAP_SIZE = 700 * 1024 * 1024; // 700 MB
const GENERAL_MAX_HEAP_SIZE = 700 * 2 * 1024 * 1024; // 1400 MB
export const MAX_FILE_SIZE = process.arch === 'ia32' ? WIN32_MAX_FILE_SIZE : GENERAL_MAX_FILE_SIZE;
export const MAX_HEAP_SIZE = process.arch === 'ia32' ? WIN32_MAX_HEAP_SIZE : GENERAL_MAX_HEAP_SIZE;

View file

@ -84,7 +84,7 @@ export class FileService implements IFileService {
};
// create service
this.raw = new NodeFileService(contextService, textResourceConfigurationService, configurationService, lifecycleService, fileServiceConfig);
this.raw = new NodeFileService(contextService, environmentService, textResourceConfigurationService, configurationService, lifecycleService, fileServiceConfig);
// Listeners
this.registerListeners();

View file

@ -11,7 +11,7 @@ import os = require('os');
import crypto = require('crypto');
import assert = require('assert');
import { isParent, FileOperation, FileOperationEvent, IContent, IFileService, IResolveFileOptions, IResolveFileResult, IResolveContentOptions, IFileStat, IStreamContent, FileOperationError, FileOperationResult, IUpdateContentOptions, FileChangeType, IImportResult, FileChangesEvent, ICreateFileOptions, IContentData, ITextSnapshot } from 'vs/platform/files/common/files';
import { MAX_FILE_SIZE } from 'vs/platform/files/node/files';
import { MAX_FILE_SIZE, MAX_HEAP_SIZE } from 'vs/platform/files/node/files';
import { isEqualOrParent } from 'vs/base/common/paths';
import { ResourceMap } from 'vs/base/common/map';
import arrays = require('vs/base/common/arrays');
@ -36,6 +36,7 @@ import Event, { Emitter } from 'vs/base/common/event';
import { FileWatcher as NsfwWatcherService } from 'vs/workbench/services/files/node/watcher/nsfw/watcherService';
import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation';
import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
import { getBaseLabel } from 'vs/base/common/labels';
@ -131,6 +132,7 @@ export class FileService implements IFileService {
constructor(
private contextService: IWorkspaceContextService,
private environmentService: IEnvironmentService,
private textResourceConfigurationService: ITextResourceConfigurationService,
private configurationService: IConfigurationService,
private lifecycleService: ILifecycleService,
@ -316,12 +318,20 @@ export class FileService implements IFileService {
}
// Return early if file is too large to load
if (typeof stat.size === 'number' && stat.size > MAX_FILE_SIZE) {
return onStatError(new FileOperationError(
nls.localize('fileTooLargeError', "File too large to open"),
FileOperationResult.FILE_TOO_LARGE,
options
));
if (typeof stat.size === 'number') {
if (stat.size > Math.max(this.environmentService.args['max_old_space_size'] * 1024 * 1024 || 0, MAX_HEAP_SIZE)) {
return onStatError(new FileOperationError(
nls.localize('fileTooLargeForHeapError', "File size exceeds V8 heap limit, please try to run code --max_old_space_size=NEWSIZE"),
FileOperationResult.FILE_EXCEED_HEAP
));
}
if (stat.size > MAX_FILE_SIZE) {
return onStatError(new FileOperationError(
nls.localize('fileTooLargeError', "File too large to open"),
FileOperationResult.FILE_TOO_LARGE
));
}
}
return void 0;
@ -455,6 +465,13 @@ export class FileService implements IFileService {
currentPosition += bytesRead;
}
if (totalBytesRead > Math.max(this.environmentService.args['max_old_space_size'] * 1024 * 1024 || 0, MAX_HEAP_SIZE)) {
finish(new FileOperationError(
nls.localize('fileTooLargeForHeapError', "File size exceeds V8 heap limit, please try to run code --max_old_space_size=NEWSIZE"),
FileOperationResult.FILE_EXCEED_HEAP
));
}
if (totalBytesRead > MAX_FILE_SIZE) {
// stop when reading too much
finish(new FileOperationError(