Pourquoi suis-je « Pas assez de stockage est disponible pour traiter cette commande » en utilisant Java MappedByteBuffers?
-
20-09-2019 - |
Question
J'ai un très large éventail de doubles que j'utilise un fichier sur disque et une liste d'échange de MappedByteBuffers à manipuler, voir cette question pour plus d'arrière-plan. Je suis en cours d'exécution sur Windows XP en utilisant Java 1.5.
Voici la partie clé de mon code qui fait l'attribution des tampons contre le fichier ...
try
{
// create a random access file and size it so it can hold all our data = the extent x the size of a double
f = new File(_base_filename);
_filename = f.getAbsolutePath();
_ioFile = new RandomAccessFile(f, "rw");
_ioFile.setLength(_extent * BLOCK_SIZE);
_ioChannel = _ioFile.getChannel();
// make enough MappedByteBuffers to handle the whole lot
_pagesize = bytes_extent;
long pages = 1;
long diff = 0;
while (_pagesize > MAX_PAGE_SIZE)
{
_pagesize /= PAGE_DIVISION;
pages *= PAGE_DIVISION;
// make sure we are at double boundaries. We cannot have a double spanning pages
diff = _pagesize % BLOCK_SIZE;
if (diff != 0) _pagesize -= diff;
}
// what is the difference between the total bytes associated with all the pages and the
// total overall bytes? There is a good chance we'll have a few left over because of the
// rounding down that happens when the page size is halved
diff = bytes_extent - (_pagesize * pages);
if (diff > 0)
{
// check whether adding on the remainder to the last page will tip it over the max size
// if not then we just need to allocate the remainder to the final page
if (_pagesize + diff > MAX_PAGE_SIZE)
{
// need one more page
pages++;
}
}
// make the byte buffers and put them on the list
int size = (int) _pagesize ; // safe cast because of the loop which drops maxsize below Integer.MAX_INT
int offset = 0;
for (int page = 0; page < pages; page++)
{
offset = (int) (page * _pagesize );
// the last page should be just big enough to accommodate any left over odd bytes
if ((bytes_extent - offset) < _pagesize )
{
size = (int) (bytes_extent - offset);
}
// map the buffer to the right place
MappedByteBuffer buf = _ioChannel.map(FileChannel.MapMode.READ_WRITE, offset, size);
// stick the buffer on the list
_bufs.add(buf);
}
Controller.g_Logger.info("Created memory map file :" + _filename);
Controller.g_Logger.info("Using " + _bufs.size() + " MappedByteBuffers");
_ioChannel.close();
_ioFile.close();
}
catch (Exception e)
{
Controller.g_Logger.error("Error opening memory map file: " + _base_filename);
Controller.g_Logger.error("Error creating memory map file: " + e.getMessage());
e.printStackTrace();
Clear();
if (_ioChannel != null) _ioChannel.close();
if (_ioFile != null) _ioFile.close();
if (f != null) f.delete();
throw e;
}
Je reçois l'erreur mentionnée dans le titre après que je le tampon allouer deuxième ou troisième.
Je pensais que c'était quelque chose à voir avec la mémoire contigu disponible, ont donc essayé avec différentes tailles et le nombre de pages, mais sans bénéfice global.
Que signifie exactement « Pas assez de stockage est disponible pour traiter cette commande » signifie et que, si quelque chose, puis-je faire?
Je pensais que le point de MappedByteBuffers était la capacité d'être en mesure de gérer des structures plus grandes que vous pourriez tenir sur le tas, et les traiter comme si elles étaient en mémoire.
Les indices?
EDIT:
En réponse à une réponse ci-dessous (@adsk) J'ai changé mon code, donc je ne jamais avoir plus d'un seul MappedByteBuffer actif à un moment donné. Quand je me réfère à une région du fichier qui est actuellement unmapped je JUNK la carte existante et de créer une nouvelle. Je reçois toujours la même erreur après environ 3 opérations de carte.
Le bug cité par GC ne recueille pas les MappedByteBuffers semble encore être un problème dans JDK 1.5.
La solution
Je pensais que le point de MappedByteBuffers était la capacité d'être en mesure de gérer des structures plus grandes que vous pourriez tenir sur le tas, et les traiter comme si elles étaient en mémoire.
Non. L'idée est / était de vous permettre d'adresser plus de 2 ** 31 doubles ... en supposant que vous aviez assez de mémoire, et utilisaient une machine virtuelle Java 64 bits.
(je suppose que cela est une question de suivi cette question.)
EDIT :. De toute évidence, plus d'explications est nécessaire
Il y a un certain nombre de limites qui entrent en jeu.
-
Java a une restriction fondamentale que l'attribut
length
d'un tableau, et les indices de tableau ont le typeint
. Ceci, combiné avec le fait queint
est signé et un tableau ne peut pas avoir une taille négative signifie que le plus grand éventail possible peut avoir des éléments de2**31
. Cette restriction s'applique à JVMs 32bits et 64bits. Il est un élément fondamental du langage Java ... comme le fait que les valeurs dechar
vont de0
à65535
. -
En utilisant une machine virtuelle Java 32 bits PLACES un (théorique) limite supérieure de
2**32
sur le nombre d'octets qui sont adressables par la machine virtuelle Java. Cela comprend, tout le tas, votre code, et les classes de bibliothèque que vous utilisez, le noyau de code natif de la machine virtuelle Java, la mémoire utilisée pour les tampons cartographiés ... tout. (En fait, en fonction de votre plate-forme, le système d'exploitation peut vous donner beaucoup moins que les octets de2**32
si l'espace d'adressage.) -
Les paramètres que vous donnez sur la ligne de commande java déterminent la quantité de mémoire tas JVM allow votre application à utiliser. La mémoire mappée à l'aide d'objets
MappedByteBuffer
ne compte pas dans ce sens. -
La quantité de mémoire que le système d'exploitation vous donnera dépend (sous Linux / UNIX) sur la quantité totale d'espace d'échange configuré, les limites de « processus » et ainsi de suite. Des limites similaires s'appliquent probablement à Windows. Et bien sûr, vous ne pouvez exécuter une machine virtuelle Java 64 bits si le système d'exploitation hôte est capable 64bit, et que vous utilisez 64bit matériel capable. (Si vous avez un Pentium, vous êtes simplement hors de la chance.)
-
Enfin, la quantité de mémoire physique de votre système entre en jeu. En théorie, vous pouvez demander à votre machine virtuelle Java d'utiliser un tas, etc qui est plusieurs fois plus grand que que la mémoire physique de votre machine. En pratique, cela est une mauvaise idée . Si vous allouez sur la mémoire virtuelle, votre système thrash et les performances des applications passera par le sol.
L'emporter est la suivante:
-
Si vous utilisez une machine virtuelle Java 32 bits, vous êtes probablement limité à quelque part entre
2**31
et2**32
octets de mémoire adressable. C'est assez d'espace pour un maximum entre doubles de2**29
et2**30
, si vous utilisez un tableau ou un tampon mappé. -
Si vous utilisez une machine virtuelle Java 64 bits, vous pouvez représenter un seul tableau de doubles de
2**31
. La limite théorique d'un tampon cartographié serait2**63
octets ou doubles de2**61
, mais la limite pratique serait à peu près la quantité de mémoire physique de votre machine a.
Autres conseils
Lorsque la mémoire en correspondance d'un fichier, il est possible de manquer d'espace d'adresse dans VM 32-bit. Cela se produit même si le fichier est mis en correspondance en petits morceaux et les ByteBuffers ne sont plus accessibles. La raison est que GC ne donne le coup pour libérer les tampons.
Reportez-vous le bogue http://bugs.sun.com/bugdatabase /view_bug.do?bug_id=6417205