g1PageBasedVirtualSpace.cpp 10.2 KB
Newer Older
1
/*
2
 * Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved.
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
 * 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.
 *
 */

#include "precompiled.hpp"
#include "gc_implementation/g1/g1PageBasedVirtualSpace.hpp"
#include "oops/markOop.hpp"
#include "oops/oop.inline.hpp"
#include "services/memTracker.hpp"
#ifdef TARGET_OS_FAMILY_linux
# include "os_linux.inline.hpp"
#endif
#ifdef TARGET_OS_FAMILY_solaris
# include "os_solaris.inline.hpp"
#endif
#ifdef TARGET_OS_FAMILY_windows
# include "os_windows.inline.hpp"
#endif
#ifdef TARGET_OS_FAMILY_aix
# include "os_aix.inline.hpp"
#endif
#ifdef TARGET_OS_FAMILY_bsd
# include "os_bsd.inline.hpp"
#endif
#include "utilities/bitMap.inline.hpp"

47 48
G1PageBasedVirtualSpace::G1PageBasedVirtualSpace(ReservedSpace rs, size_t used_size, size_t page_size) :
  _low_boundary(NULL), _high_boundary(NULL), _committed(), _page_size(0), _special(false),
49
  _dirty(), _executable(false) {
50
  initialize_with_page_size(rs, used_size, page_size);
51 52
}

53 54 55
void G1PageBasedVirtualSpace::initialize_with_page_size(ReservedSpace rs, size_t used_size, size_t page_size) {
  guarantee(rs.is_reserved(), "Given reserved space must have been reserved already.");

56
  assert(_low_boundary == NULL, "VirtualSpace already initialized");
57 58 59 60 61 62 63 64 65 66
  assert(page_size > 0, "Page size must be non-zero.");

  guarantee(is_ptr_aligned(rs.base(), page_size),
            err_msg("Reserved space base " PTR_FORMAT " is not aligned to requested page size " SIZE_FORMAT, p2i(rs.base()), page_size));
  guarantee(is_size_aligned(used_size, os::vm_page_size()),
            err_msg("Given used reserved space size needs to be OS page size aligned (%d bytes) but is " SIZE_FORMAT, os::vm_page_size(), used_size));
  guarantee(used_size <= rs.size(),
            err_msg("Used size of reserved space " SIZE_FORMAT " bytes is smaller than reservation at " SIZE_FORMAT " bytes", used_size, rs.size()));
  guarantee(is_size_aligned(rs.size(), page_size),
            err_msg("Expected that the virtual space is size aligned, but " SIZE_FORMAT " is not aligned to page size " SIZE_FORMAT, rs.size(), page_size));
67 68

  _low_boundary  = rs.base();
69
  _high_boundary = _low_boundary + used_size;
70 71 72 73 74 75 76

  _special = rs.special();
  _executable = rs.executable();

  _page_size = page_size;

  assert(_committed.size() == 0, "virtual space initialized more than once");
77 78
  BitMap::idx_t size_in_pages = rs.size() / page_size;
  _committed.resize(size_in_pages, /* in_resource_area */ false);
79
  if (_special) {
80
    _dirty.resize(size_in_pages, /* in_resource_area */ false);
81
  }
82

83
  _tail_size = used_size % _page_size;
84 85 86 87 88 89 90 91 92 93 94 95 96 97
}

G1PageBasedVirtualSpace::~G1PageBasedVirtualSpace() {
  release();
}

void G1PageBasedVirtualSpace::release() {
  // This does not release memory it never reserved.
  // Caller must release via rs.release();
  _low_boundary           = NULL;
  _high_boundary          = NULL;
  _special                = false;
  _executable             = false;
  _page_size              = 0;
98
  _tail_size              = 0;
99
  _committed.resize(0, false);
100
  _dirty.resize(0, false);
101 102 103
}

size_t G1PageBasedVirtualSpace::committed_size() const {
104 105 106 107 108 109
  size_t result = _committed.count_one_bits() * _page_size;
  // The last page might not be in full.
  if (is_last_page_partial() && _committed.at(_committed.size() - 1)) {
    result -= _page_size - _tail_size;
  }
  return result;
110 111 112 113 114 115 116 117 118 119
}

size_t G1PageBasedVirtualSpace::reserved_size() const {
  return pointer_delta(_high_boundary, _low_boundary, sizeof(char));
}

size_t G1PageBasedVirtualSpace::uncommitted_size()  const {
  return reserved_size() - committed_size();
}

120
size_t G1PageBasedVirtualSpace::addr_to_page_index(char* addr) const {
121 122 123
  return (addr - _low_boundary) / _page_size;
}

124 125 126
bool G1PageBasedVirtualSpace::is_area_committed(size_t start_page, size_t size_in_pages) const {
  size_t end_page = start_page + size_in_pages;
  return _committed.get_next_zero_offset(start_page, end_page) >= end_page;
127 128
}

129 130 131
bool G1PageBasedVirtualSpace::is_area_uncommitted(size_t start_page, size_t size_in_pages) const {
  size_t end_page = start_page + size_in_pages;
  return _committed.get_next_one_offset(start_page, end_page) >= end_page;
132 133
}

134
char* G1PageBasedVirtualSpace::page_start(size_t index) const {
135 136 137
  return _low_boundary + index * _page_size;
}

138 139 140 141
bool G1PageBasedVirtualSpace::is_after_last_page(size_t index) const {
  guarantee(index <= _committed.size(),
            err_msg("Given boundary page " SIZE_FORMAT " is beyond managed page count " SIZE_FORMAT, index, _committed.size()));
  return index == _committed.size();
142 143
}

144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203
void G1PageBasedVirtualSpace::commit_preferred_pages(size_t start, size_t num_pages) {
  assert(num_pages > 0, "No full pages to commit");
  assert(start + num_pages <= _committed.size(),
           err_msg("Tried to commit area from page " SIZE_FORMAT " to page " SIZE_FORMAT " "
                   "that is outside of managed space of " SIZE_FORMAT " pages",
                   start, start + num_pages, _committed.size()));

  char* start_addr = page_start(start);
  size_t size = num_pages * _page_size;

  os::commit_memory_or_exit(start_addr, size, _page_size, _executable,
                            err_msg("Failed to commit area from " PTR_FORMAT " to " PTR_FORMAT " of length " SIZE_FORMAT ".",
                                    p2i(start_addr), p2i(start_addr + size), size));
}

void G1PageBasedVirtualSpace::commit_tail() {
  assert(_tail_size > 0, "The size of the tail area must be > 0 when reaching here");

  char* const aligned_end_address = (char*)align_ptr_down(_high_boundary, _page_size);
  os::commit_memory_or_exit(aligned_end_address, _tail_size, os::vm_page_size(), _executable,
                            err_msg("Failed to commit tail area from " PTR_FORMAT " to " PTR_FORMAT " of length " SIZE_FORMAT ".",
                                    p2i(aligned_end_address), p2i(_high_boundary), _tail_size));
}

void G1PageBasedVirtualSpace::commit_internal(size_t start_page, size_t end_page) {
  guarantee(start_page < end_page,
            err_msg("Given start page " SIZE_FORMAT " is larger or equal to end page " SIZE_FORMAT, start_page, end_page));
  guarantee(end_page <= _committed.size(),
            err_msg("Given end page " SIZE_FORMAT " is beyond end of managed page amount of " SIZE_FORMAT, end_page, _committed.size()));

  size_t pages = end_page - start_page;
  bool need_to_commit_tail = is_after_last_page(end_page) && is_last_page_partial();

  // If we have to commit some (partial) tail area, decrease the amount of pages to avoid
  // committing that in the full-page commit code.
  if (need_to_commit_tail) {
    pages--;
  }

  if (pages > 0) {
    commit_preferred_pages(start_page, pages);
  }

  if (need_to_commit_tail) {
    commit_tail();
  }
}

char* G1PageBasedVirtualSpace::bounded_end_addr(size_t end_page) const {
  return MIN2(_high_boundary, page_start(end_page));
}

void G1PageBasedVirtualSpace::pretouch_internal(size_t start_page, size_t end_page) {
  guarantee(start_page < end_page,
            err_msg("Given start page " SIZE_FORMAT " is larger or equal to end page " SIZE_FORMAT, start_page, end_page));

  os::pretouch_memory(page_start(start_page), bounded_end_addr(end_page));
}

bool G1PageBasedVirtualSpace::commit(size_t start_page, size_t size_in_pages) {
204
  // We need to make sure to commit all pages covered by the given area.
205
  guarantee(is_area_uncommitted(start_page, size_in_pages), "Specified area is not uncommitted");
206

207
  bool zero_filled = true;
208
  size_t end_page = start_page + size_in_pages;
209 210 211

  if (_special) {
    // Check for dirty pages and update zero_filled if any found.
212
    if (_dirty.get_next_one_offset(start_page, end_page) < end_page) {
213
      zero_filled = false;
214
      _dirty.clear_range(start_page, end_page);
215 216
    }
  } else {
217
    commit_internal(start_page, end_page);
218
  }
219
  _committed.set_range(start_page, end_page);
220

T
tschatzl 已提交
221
  if (AlwaysPreTouch) {
222
    pretouch_internal(start_page, end_page);
T
tschatzl 已提交
223
  }
224
  return zero_filled;
225 226
}

227 228 229 230 231 232 233 234 235 236
void G1PageBasedVirtualSpace::uncommit_internal(size_t start_page, size_t end_page) {
  guarantee(start_page < end_page,
            err_msg("Given start page " SIZE_FORMAT " is larger or equal to end page " SIZE_FORMAT, start_page, end_page));

  char* start_addr = page_start(start_page);
  os::uncommit_memory(start_addr, pointer_delta(bounded_end_addr(end_page), start_addr, sizeof(char)));
}

void G1PageBasedVirtualSpace::uncommit(size_t start_page, size_t size_in_pages) {
  guarantee(is_area_committed(start_page, size_in_pages), "checking");
237

238
  size_t end_page = start_page + size_in_pages;
239 240 241
  if (_special) {
    // Mark that memory is dirty. If committed again the memory might
    // need to be cleared explicitly.
242
    _dirty.set_range(start_page, end_page);
243
  } else {
244
    uncommit_internal(start_page, end_page);
245 246
  }

247
  _committed.clear_range(start_page, end_page);
248 249 250 251 252 253 254 255 256
}

bool G1PageBasedVirtualSpace::contains(const void* p) const {
  return _low_boundary <= (const char*) p && (const char*) p < _high_boundary;
}

#ifndef PRODUCT
void G1PageBasedVirtualSpace::print_on(outputStream* out) {
  out->print   ("Virtual space:");
257
  if (_special) out->print(" (pinned in memory)");
258 259 260
  out->cr();
  out->print_cr(" - committed: " SIZE_FORMAT, committed_size());
  out->print_cr(" - reserved:  " SIZE_FORMAT, reserved_size());
261 262
  out->print_cr(" - preferred page size: " SIZE_FORMAT, _page_size);
  out->print_cr(" - [low_b, high_b]: [" PTR_FORMAT ", " PTR_FORMAT "]",  p2i(_low_boundary), p2i(_high_boundary));
263 264 265 266 267 268
}

void G1PageBasedVirtualSpace::print() {
  print_on(tty);
}
#endif