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 // 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: // 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 // 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 // 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 // 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; 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 // Load our code once ready
app.once('ready', function () { app.once('ready', function () {
perf.mark('main:appReady'); 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 = { const options = {
detached: true, detached: true,
env env

View file

@ -53,6 +53,7 @@ export interface ParsedArgs {
'disable-updates'?: string; 'disable-updates'?: string;
'disable-crash-reporter'?: string; 'disable-crash-reporter'?: string;
'skip-add-to-recently-opened'?: boolean; 'skip-add-to-recently-opened'?: boolean;
'max_old_space_size'?: number;
'file-write'?: boolean; 'file-write'?: boolean;
'file-chmod'?: boolean; 'file-chmod'?: boolean;
'upload-logs'?: string; '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."), '--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."), '--disable-gpu': localize('disableGPU', "Disable GPU hardware acceleration."),
'--upload-logs': localize('uploadLogs', "Uploads logs from current session to a secure endpoint."), '--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 { export function formatOptions(options: { [name: string]: string; }, columns: number): string {

View file

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

View file

@ -7,4 +7,9 @@
const WIN32_MAX_FILE_SIZE = 300 * 1024 * 1024; // 300 MB const WIN32_MAX_FILE_SIZE = 300 * 1024 * 1024; // 300 MB
const GENERAL_MAX_FILE_SIZE = 16 * 1024 * 1024 * 1024; // 16 GB 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 // create service
this.raw = new NodeFileService(contextService, textResourceConfigurationService, configurationService, lifecycleService, fileServiceConfig); this.raw = new NodeFileService(contextService, environmentService, textResourceConfigurationService, configurationService, lifecycleService, fileServiceConfig);
// Listeners // Listeners
this.registerListeners(); this.registerListeners();

View file

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