/* * Copyright (c) 2007, 2012, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.max.memory; import static com.sun.max.vm.intrinsics.MaxineIntrinsicIDs.*; import java.io.*; import com.sun.max.annotate.*; import com.sun.max.platform.*; import com.sun.max.profile.*; import com.sun.max.unsafe.*; import com.sun.max.util.timer.*; import com.sun.max.vm.*; import com.sun.max.vm.MaxineVM.Phase; /** * Access to the virtual memory facilities of the underlying operating system. * Provides methods to allocate quantities of memory that are expected to be * multiples of page size (or may be rounded up). To enable possible optimizations * in virtual memory management, memory is classified into different different uses * by the {@link Type} enum. * * Also provides the ability to map files into virtual memory and to change page protection. */ public final class VirtualMemory { private static boolean TraceAnonOperations = false; static { VMOptions.addFieldOption("-XX:", "TraceAnonOperations", VirtualMemory.class, "TraceAnonOperations", Phase.PRISTINE); } public enum Type { HEAP, // for the garbage collected heap STACK, // for thread stacks CODE, // for compiled code DATA // for miscellaneous data } private VirtualMemory() { } /** * Allocates virtual memory of a given type. * * @param size the amount requested * @param type the type of memory requested * @return the address of the allocated memory or {@link Pointer#zero()} if the allocation failed */ public static Pointer allocate(Size size, Type type) { allocateMemoryTime.start(); final Pointer allocated = virtualMemory_allocate(size, type.ordinal()); allocateMemoryTime.stop(); return allocated; } /** * Deallocates virtual memory of a given type. * * @param pointer base address of previously allocated memory * @param size size of previously allocated memory * @param type type of memory * @return {@link Address#zero()} if failed, {@code pointer} otherwise. */ public static Address deallocate(Address pointer, Size size, Type type) { if (TraceAnonOperations) { traceRange("deallocate", pointer, size); } deallocateMemoryTime.start(); Address deallocated = virtualMemory_deallocate(pointer, size, type.ordinal()); deallocateMemoryTime.stop(); return deallocated; } /** * Allocates virtual memory at a fixed address. * Evidently the caller of this method must know that the virtual memory * at the given address is available for allocation. * * @param address page aligned address at which to allocate the virtual memory * @param size the size requested * @return true if the memory was allocated, false otherwise */ public static boolean allocateAtFixedAddress(Address address, Size size, Type type) { allocateAtFixedAddressTime.start(); final boolean allocated = virtualMemory_allocateAtFixedAddress(address, size, type.ordinal()); allocateAtFixedAddressTime.stop(); return allocated; } /** * Allocates virtual memory at a fixed address, which must be page aligned. * Evidently the caller of this method must know that the virtual memory * at the given address is available for allocation. * * @param pointer page aligned address at which to allocate the virtual memory * @param size the size requested * @param type of memory * @return true if the memory was allocated, false otherwise */ public static boolean allocatePageAlignedAtFixedAddress(Address pointer, Size size, Type type) { if (!pointer.isAligned(Platform.platform().pageSize)) { throw new IllegalArgumentException("start address of the request memory must be page aligned "); } return allocateAtFixedAddress(pointer, size, type); } /** * Allocates virtual memory in the address range available using 31 bits of addressing. * I.e., in the first 2GB of memory. This method may not be implemented on all platforms. * * @param size the size requested * @param type of memory * @return the address of the allocated memory or {@link Pointer#zero()} if unsuccessful */ public static Pointer allocateIn31BitSpace(Size size, Type type) { return virtualMemory_allocateIn31BitSpace(size, type.ordinal()); } /** * Generic virtual memory allocator. * @param address reserve virtual memory at specified address if not zero, otherwise let the underlying OS decide where to allocate * @param size * @param reserveSwap reserve swap space if set to true. * @param protNone no protection is set if true, all are set otherwise * @return address to the allocated chunk of virtual memory, zero otherwise if allocation failed for any reason. */ @C_FUNCTION private static native Pointer virtualMemory_allocatePrivateAnon(Address address, Size size, boolean reserveSwap, boolean protNone, int type); @C_FUNCTION private static native boolean virtualMemory_allocateAtFixedAddress(Address address, Size size, int type); @C_FUNCTION private static native Pointer virtualMemory_allocateIn31BitSpace(Size size, int type); @C_FUNCTION private static native Pointer virtualMemory_allocate(Size size, int type); @C_FUNCTION private static native Pointer virtualMemory_deallocate(Address start, Size size, int type); private static final TimerMetric allocateAtFixedAddressTime = new TimerMetric(new SingleUseTimer(Clock.SYSTEM_MILLISECONDS)); private static final TimerMetric allocateMemoryTime = new TimerMetric(new SingleUseTimer(Clock.SYSTEM_MILLISECONDS)); private static final TimerMetric reserveMemoryTime = new TimerMetric(new SingleUseTimer(Clock.SYSTEM_MILLISECONDS)); private static final TimerMetric commitMemoryTime = new TimerMetric(new SingleUseTimer(Clock.SYSTEM_MILLISECONDS)); private static final TimerMetric uncommitMemoryTime = new TimerMetric(new SingleUseTimer(Clock.SYSTEM_MILLISECONDS)); private static final TimerMetric deallocateMemoryTime = new TimerMetric(new SingleUseTimer(Clock.SYSTEM_MILLISECONDS)); public static void reportMetrics() { reserveMemoryTime.report("VirtualMemory.reserveMemory", Log.out); commitMemoryTime.report("VirtualMemory.commitMemory", Log.out); uncommitMemoryTime.report("VirtualMemory.uncommitMemory", Log.out); deallocateMemoryTime.report("VirtualMemory.deallocate", Log.out); allocateMemoryTime.report("VirtualMemory.allocate", Log.out); allocateAtFixedAddressTime.report("VirtualMemory.allocateAtFixedAddress", Log.out); } /** * Allocates virtual memory that is not backed by swap space. * * @param size the size requested * @param type of memory * @return the address of the allocated memory or zero if unsuccessful */ public static Pointer allocateNoSwap(Size size, Type type) { return virtualMemory_allocatePrivateAnon(Address.zero(), size, false, false, type.ordinal()); } public static Pointer reserveMemory(Address address, Size size, Type type) { if (TraceAnonOperations) { traceRange("reserveMemory", address, size); } reserveMemoryTime.start(); final Pointer result = virtualMemory_allocatePrivateAnon(address, size, false, true, type.ordinal()); reserveMemoryTime.stop(); return result; } public static boolean commitMemory(Address address, Size size, Type type) { if (address.isZero()) { return false; } if (TraceAnonOperations) { traceRange("commitMemory", address, size); } commitMemoryTime.start(); final Pointer committed = virtualMemory_allocatePrivateAnon(address, size, true, false, type.ordinal()); commitMemoryTime.stop(); return committed.equals(address); } public static boolean uncommitMemory(Address address, Size size, Type type) { if (address.isZero()) { return false; } if (TraceAnonOperations) { traceRange("uncommitMemory", address, size); } uncommitMemoryTime.start(); // Remap previously mapped space so the new space isn't backed with swap space and all access are prevented (protNone = true). final Pointer uncommitted = virtualMemory_allocatePrivateAnon(address, size, false, true, type.ordinal()); uncommitMemoryTime.stop(); return !uncommitted.isZero(); } /** * Return the amount of physical memory (in bytes) of the underlying platform. * @return amount of physical memory in bytes */ @INLINE public static Size getPhysicalMemorySize() { return virtualMemory_getPhysicalMemorySize(); } @C_FUNCTION private static native Size virtualMemory_getPhysicalMemorySize(); /* Page protection methods */ /** * Sets access protection for a number of memory pages such that any access (read or write) to them causes a trap. * * @param address an address denoting the first page. This value must be aligned to the * underlying platform's {@linkplain Platform#pageSize page size}. * @param count the number of pages to protect */ @INLINE public static void protectPages(Address address, int count) { virtualMemory_protectPages(address, count); } /** * Sets access protection for a number of memory pages such that all accesses are allowed. * * @param address an address denoting the first page. This value must be aligned to the * underlying platform's {@linkplain Platform#pageSize page size}. * @param count the number of pages to unprotect */ @INLINE public static void unprotectPages(Address address, int count) { virtualMemory_unprotectPages(address, count); } @C_FUNCTION private static native void virtualMemory_protectPages(Address address, int count); @C_FUNCTION private static native void virtualMemory_unprotectPages(Address address, int count); /* File mapping methods */ /** * An alias type for accessing the file descriptor field "fd" in java.io.FileDescriptor without * having to use reflection. */ public static class JIOFDAlias { @ALIAS(declaringClass = java.io.FileDescriptor.class) int fd; } @INTRINSIC(UNSAFE_CAST) public static native JIOFDAlias asJIOFDAlias(Object o); /** * Maps an open file into virtual memory. * * @param size * @param fileDescriptor * @param fileOffset * @throws IOException */ public static Pointer mapFile(Size size, FileDescriptor fileDescriptor, Address fileOffset) throws IOException { final int fd = asJIOFDAlias(fileDescriptor).fd; return Pointer.fromLong(virtualMemory_mapFile(size.toLong(), fd, fileOffset.toLong())); } /** * Maps an open file into virtual memory restricted to the address range available in 31 bits, i.e. up to 2GB. * This is only available on Linux. * * @param size * @param fileDescriptor * @param fileOffset * @throws IOException */ public static Pointer mapFileIn31BitSpace(int size, FileDescriptor fileDescriptor, Address fileOffset) throws IOException { if (Platform.platform().os != OS.LINUX) { throw new UnsupportedOperationException(); } final int fd = asJIOFDAlias(fileDescriptor).fd; return Pointer.fromLong(virtualMemory_mapFileIn31BitSpace(size, fd, fileOffset.toLong())); } /* These are JNI functions because they may block */ private static native long virtualMemory_mapFile(long size, int fd, long fileOffset); private static native long virtualMemory_mapFileIn31BitSpace(int size, int fd, long fileOffset); public static void traceRange(String label, Address start, Size size) { Log.print(label); Log.print("[ "); Log.print(start); Log.print(", "); Log.print(start.plus(size)); Log.print("] "); Log.print(size.toLong()); Log.print(" ("); Log.printToPowerOfTwoUnits(size); Log.println(")"); } }