runtime: Assembly.Load opens /dev/zero in linux and does not close it

too many files open error in linux

After several hours of work web application on cent os 7 gets error “Too many files open”

General

After each request to my application the system opens and keep open a lot of /dev/zero files. I run command lsof | grep zero | wc -l and looking out that count of files increase and increase.

image

System Info

OS LINUX X64 Linux 3.10.0-1062.4.1.el7.x86_64 dotnet/core#1 SMP Fri Oct 18 17:15:30 UTC 2019 Cent OS 7 .NET Core 3.0.0

About this issue

  • Original URL
  • State: closed
  • Created 5 years ago
  • Reactions: 1
  • Comments: 19 (12 by maintainers)

Most upvoted comments

Hi @Pilchie @carlossanlop We think this is not asp issue, but core. Assemly.Load opens /dev/zero stream

Here code to reproduce

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Emit;
using Microsoft.CodeAnalysis.Text;

namespace TestAssemblyLoad
{
    class Program
    {
        private static readonly Encoding Encoding = Encoding.UTF8;
        private static readonly CSharpParseOptions ParseOptions = CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.Latest);
        private static readonly CSharpCompilationOptions CompilationOptions = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary);
        private static readonly ConcurrentDictionary<string, MetadataReference> References = new ConcurrentDictionary<string, MetadataReference>();

        static Program()
        {
            var referencedAssemblies = Assembly.GetEntryAssembly().GetReferencedAssemblies();
            foreach (var referencedAssembly in referencedAssemblies)
                AddReference(referencedAssembly);
        }

        private static void AddReference(AssemblyName assemblyName)
        {
            var assembly = Assembly.Load(assemblyName);
            References.TryAdd(assembly.Location, MetadataReference.CreateFromFile(assembly.Location));

            var referencedAssemblyNames = assembly.GetReferencedAssemblies();
            foreach (var referencedAssemblyName in referencedAssemblyNames)
            {
                var referencedAssembly = Assembly.Load(referencedAssemblyName);
                References.TryAdd(referencedAssembly.Location, MetadataReference.CreateFromFile(referencedAssembly.Location));
            }
        }

        static void Main(string[] args)
        {
            Console.WriteLine("PID: " + Process.GetCurrentProcess().Id);
            Console.ReadKey();

            Run(@"using System;
public class SubProgram
{
    public static void Main()
    {
        Console.WriteLine(""Hello World!"");
    }
}");

            Console.WriteLine("The end...");
            Console.ReadKey();
        }

        public static void Run(string code)
        {
            var assemblyName = nameof(Assembly) + "_" + Guid.NewGuid().ToString("N");

            var syntaxTrees = new List<SyntaxTree>();
            var embeddedTexts = new List<EmbeddedText>();

            var filePath = $"{nameof(Assembly)}_file_{Guid.NewGuid():N}.cs";

            var buffer = Encoding.GetBytes(code);

            var sourceText = SourceText.From(buffer, buffer.Length, Encoding, canBeEmbedded: true);
            var syntaxTree = CSharpSyntaxTree.ParseText(sourceText, ParseOptions, filePath);
            var embeddedText = EmbeddedText.FromSource(filePath, sourceText);

            syntaxTrees.Add(syntaxTree);
            embeddedTexts.Add(embeddedText);

            var compilation = CSharpCompilation.Create(
                assemblyName,
                syntaxTrees: syntaxTrees,
                references: References.Values,
                options: CompilationOptions);

            var emitOptions = new EmitOptions(
                debugInformationFormat: DebugInformationFormat.PortablePdb,
                pdbFilePath: Path.ChangeExtension(assemblyName, "pdb"));

            using var stream = new MemoryStream();
            using var symbolsStream = new MemoryStream();

            var emitResult = compilation.Emit(stream, symbolsStream, embeddedTexts: embeddedTexts, options: emitOptions);

            if (emitResult.Success)
            {
                stream.Seek(0, SeekOrigin.Begin);
                symbolsStream.Seek(0, SeekOrigin.Begin);
            }

            var streamArr = stream.ToArray();
            var symbolsStreamArr = symbolsStream.ToArray();

            var assembly = Assembly.Load(streamArr, symbolsStreamArr);
            var type = assembly.GetType("SubProgram");
            var method = type.GetMethod("Main");
            method.Invoke(null, null);

            //stream.Dispose();
            //symbolsStream.Dispose();
        }
    }
}

after line var assembly = Assembly.Load(streamArr, symbolsStreamArr); count of /dev/zero stream increased.

We deepened to code inside the framework, file \src\System.Private.CoreLib\src\System\Runtime\Loader\AssemblyLoadContext.CoreCLR.cs has method

internal unsafe Assembly InternalLoad(ReadOnlySpan<byte> arrAssembly, ReadOnlySpan<byte> arrSymbols){
...
	fixed (byte* ptrAssembly = arrAssembly, ptrSymbols = arrSymbols)
	{
		LoadFromStream(_nativeAssemblyLoadContext, new IntPtr(ptrAssembly), arrAssembly.Length,
			new IntPtr(ptrSymbols), arrSymbols.Length, JitHelpers.GetObjectHandleOnStack(ref loadedAssembly));
	}
...
}

and dll import

[DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
private static extern IntPtr LoadFromStream(IntPtr ptrNativeAssemblyLoadContext, IntPtr ptrAssemblyArray, int iAssemblyArrayLen, IntPtr ptrSymbols, int iSymbolArrayLen, ObjectHandleOnStack retAssembly);

The LoadFromStream opens IntPtr and does not close it. May be it should be void, like

[DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
private static extern void LoadFromPath(IntPtr ptrNativeAssemblyLoadContext, string? ilPath, string? niPath, ObjectHandleOnStack retAssembly);

AspRuntimeCompilation loads assembly with the same way while processing and compile views.