diff --git a/src/main.js b/src/main.js index d458afd8dad..f8cbd3592c1 100644 --- a/src/main.js +++ b/src/main.js @@ -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'); diff --git a/src/vs/code/node/cli.ts b/src/vs/code/node/cli.ts index 61bb55cf91e..d83d99c8314 100644 --- a/src/vs/code/node/cli.ts +++ b/src/vs/code/node/cli.ts @@ -306,6 +306,13 @@ export async function main(argv: string[]): TPromise { }); } + 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 diff --git a/src/vs/platform/environment/common/environment.ts b/src/vs/platform/environment/common/environment.ts index ea31afddd32..9523077217e 100644 --- a/src/vs/platform/environment/common/environment.ts +++ b/src/vs/platform/environment/common/environment.ts @@ -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; diff --git a/src/vs/platform/environment/node/argv.ts b/src/vs/platform/environment/node/argv.ts index bea31250516..9b66c4d0b0e 100644 --- a/src/vs/platform/environment/node/argv.ts +++ b/src/vs/platform/environment/node/argv.ts @@ -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 { diff --git a/src/vs/platform/files/common/files.ts b/src/vs/platform/files/common/files.ts index 1cccbb587e0..c8bf3017378 100644 --- a/src/vs/platform/files/common/files.ts +++ b/src/vs/platform/files/common/files.ts @@ -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 = { diff --git a/src/vs/platform/files/node/files.ts b/src/vs/platform/files/node/files.ts index b6263d0970f..629a67e8499 100644 --- a/src/vs/platform/files/node/files.ts +++ b/src/vs/platform/files/node/files.ts @@ -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; \ No newline at end of file +// 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; \ No newline at end of file diff --git a/src/vs/workbench/services/files/electron-browser/fileService.ts b/src/vs/workbench/services/files/electron-browser/fileService.ts index 99b4e90c588..bf38886e362 100644 --- a/src/vs/workbench/services/files/electron-browser/fileService.ts +++ b/src/vs/workbench/services/files/electron-browser/fileService.ts @@ -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(); diff --git a/src/vs/workbench/services/files/node/fileService.ts b/src/vs/workbench/services/files/node/fileService.ts index 402c8956b53..285b8124e59 100644 --- a/src/vs/workbench/services/files/node/fileService.ts +++ b/src/vs/workbench/services/files/node/fileService.ts @@ -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(