2009-12-28 3 views
1

Désolé pour une description longue, mais les questions ne sont pas si faciles ...Suivre tous les alloc/allocWithZone/dealloc

Mon projet écrit sans GC. Récemment j'ai trouvé une fuite de mémoire que je ne peux pas trouver. J'ai utilisé Xcode Analyzer sans résultat. J'ai lu mon code ligne par ligne et vérifié tous les alloc/release/copy/autorelease/mutableCopy/retain et pools ... - toujours rien. Préambule: Standard Instruments et Omni Leak Checker ne fonctionnent pas pour moi pour une raison quelconque (Omin Tool rejette mon application, Instruments.app (Leaks) mange trop de mémoire et de CPU, donc je n'ai aucune chance de l'utiliser).

Donc, je veux écrire et utiliser mon propre code pour accrocher & suivre toutes les statistiques alloc/allocWithZone:/dealloc messages pour écrire une bibliothèque de vérification des fuites (l'objectif principal est seulement de marquer les noms des classes d'objets avec possible fuites).

La principale technique d'accrochage que j'utilise:

Method originalAllocWithZone = class_getClassMethod([NSObject class],@selector(allocWithZone:)); 
    if (originalAllocWithZone) 
    { 
    imp_azo = (t_impAZOriginal)method_getImplementation(originalAllocWithZone); 
    if (imp_azo) 
    { 
    Method hookedAllocWithZone = class_getClassMethod([NSObject class],@selector(hookedAllocWithZone:)); 
    if (hookedAllocWithZone) 
    { 
    method_setImplementation(originalAllocWithZone,method_getImplementation(hookedAllocWithZone)); 
    fprintf(stderr,"Leaks Hook: allocWithZone: ; Installed\n"); 
    } 
    } 
    } 
    Code
  • comme cela pour crochet de la méthode alloc, et dealloc comme méthode de NSObject catégorie.

Je sauverai IMP pour la mise en œuvre des méthodes précédentes registre & puis calculera tous alloc/allocWithZone: appels comme incrément (+1) stat-tableau des valeurs NSInteger et les appels dealloc comme décrémentation (-1).

En tant que point final, j'appelle l'implémentation précédente et la valeur de retour.

En principe tout fonctionne très bien. En cas de besoin, je peux même détecter quand les classes font partie du cluster de classe (comme NSString, NSPathStore2, NSDate, __NSCFDate) ... via une fonction normalize (mais cela n'a pas d'importance pour les problèmes décrits ci-dessous) .

Cependant cette technique a quelques problèmes:

  • Toutes les classes peuvent être pris, par exemple , [date NSDate] ne se coince pas dans alloc/allocWithZone: du tout, cependant, je peux voir Comme j'essaie d'utiliser la technique de détection automatique de singleton (basée sur readContent readind) pour exclure automatiquement certains objets des statistiques finales, la création de NSLocale se bloque à l'étape pré-init lors du démarrage de l'application Cocoa complète (en fait, même simple utilitaire de ligne de commande Objective-C avec le framework Foundation inclus a une initialisation supplémentaire avant main()) - par GDB il y a allocWithZone: appelle l'un après l'autre, ....

projet Sources complètes Concept-projet téléchargé ici: http://unclemif.com/external/DILeak.zip (3,5 Kb)

Run make de Terminal.app pour le compiler, exécutez ./concept pour le montrer en action.


La 1ère question : Pourquoi je ne peux pas attraper toutes les allocations d'objet en accrochant alloc & allocWithZone: méthodes?

Le 2ème question: Pourquoi accroché allocWithZone: gèle à CFGetRetainCount (ou [inst retainCount]) pour certaines classes ...

+0

Wow. Cela va vraiment loin pour éviter d'utiliser les solutions évidentes, car elles sont trop lentes ou utilisent trop de mémoire, et pourtant, on se demande si un tel argument est valable même quand l'outillage (quel que soit le type que vous utilisez) ne doit pas être laissé dans la construction finale, mais seulement pour être utilisé lorsqu'un problème est suspecté. Je pense que pour 99,999% des gens, utiliser les outils standards est la bonne réponse, et rouler le vôtre est une solution bizarro que peu d'autres développeurs trouveront utile. –

Répondre

4

Même sans l'instrument Leaks, les instruments peuvent toujours vous aider. Commencez avec le modèle Leaks, puis supprimez l'instrument Leaks (puisque vous dites qu'il utilise trop de mémoire). ObjectAlloc seul vous dira toutes les allocations et désallocations de vos objets, et (avec une option activée, ce qui est par défaut dans le modèle Leaks) toutes leurs rétentions et versions.

Vous pouvez définir l'objet ObjectAlloc pour afficher uniquement les objets qui existent encore; Si vous apportez l'application au point où aucun objet (ou aucun objet d'une certaine classe) ne doit exister et que de tels objets existent encore, vous avez une fuite. Vous pouvez ensuite explorer la cause de la fuite.

This video may help.

+0

Merci. De cette façon est préféré, mais, comme je l'ai déjà dit - parfois l'outil Instruments mange trop de mémoire et tracée App est ralentit irrémédiablement ... Mais en limitant certaines données d'entrée et le temps de surveillance - il existe une coopération possible avec Instruments. – UncleMiF

+0

J'utilise maintenant la solution suivante pour récupérer les fuites consommant de la mémoire: Démarrer les instruments, ouvrir le modèle des fuites, supprimer la piste d'objet alloc, exécuter l'application avec des fuites uniquement (également définir le délai d'auto-détection sur 30-60 secondes). Il donne 10 fois plus de mémoire et d'économie de temps tout en traçant des fuites! – UncleMiF

+0

Cool solution Peter, cette chose Instruments est incroyablement flexible. –

-2

Démarrer à partir des modèles de Xcode. N'essayez pas de lancer votre propre routine main() pour une application de cacao jusqu'à ce que vous sachiez ce que vous faites.

+3

Il y a beaucoup de raisons de rouler votre propre main(). Ce n'est pas l'un d'entre eux, cependant. – bbum

+0

Oui, l'exemple de démonstration n'est pas Cocoa - c'est juste un utilitaire de ligne de commande qui utilise Foundation framework pour simplifier la démo. La différence entre Cocoa Way et cette simple application est l'initialisation cachée avant main()/NSApplicationMain(). Essayez de décommenter la ligne NSLocale dans le concept.m - pour voir le problème de blocage que vous verrez si vous construisez la bibliothèque DILeak avec l'application Cocoa originale. Regardez [Date NSDate] pour vérifier qu'il n'a pas été enregistré par alloc-tracker. – UncleMiF

6

Holy réinventant la roue, batman!

Vous rendez cette voie plus difficile que nécessaire. Il n'y a absolument aucun besoin de rouler vos propres outils de suivi d'objet (bien que ce soit un exercice mental intéressant). Comme vous utilisez le CPG, les outils de suivi des allocations et d'identification des fuites sont tous très matures. Sous GC, une fuite prendra l'une des deux formes suivantes:

soit il y aura une forte référence à l'objet qui aurait dû être détruit depuis longtemps ou l'objet a été CFRetain 'd sans équilibrage CFRelease.

Le collectionneur est très habile à comprendre pourquoi un objet donné reste au-delà de son accueil.

Ainsi, vous devez trouver un ensemble d'objets qui traînent trop longtemps. Tout objet fera l'affaire. Une fois que vous avez l'adresse de l'objet, vous pouvez utiliser l'instrument Object Graph dans Instruments pour comprendre pourquoi il se maintient; comprendre ce qui s'y rapporte encore ou où il a été retenu.

Ou, à partir de gdb, utilisez info gc-roots 0xaddr pour trouver toutes les choses qui enracinent l'objet. Si vous activez l'historique de malloc (voir la page man malloc), vous pouvez obtenir les historiques d'allocation des objets qui contiennent la référence.


Oh, sans GC, hein ...

Vous êtes toujours à gauche avec une pléthore d'outils et pas besoin de réinventer la roue.

L'outil de ligne de commande leaks vous donnera souvent de bons indices. Activez MallocStackLoggingNoCompact pour pouvoir utiliser malloc_history (un autre outil de ligne de commande).

Ou utilisez l'instrument ObjectAlloc.

Dans tous les cas, vous devez identifier un ou deux objets en cours de fuite. Avec cela, vous pouvez comprendre ce qui s'y accroche. En non-GC, c'est entièrement un cas de comprendre pourquoi il y a une retenue non équilibrée par une libération.

+0

Il a dit qu'il l'écrit "sans" GC. Réponse informative pour ceux d'entre nous écrivant AVEC GC, cependant. – jbrennan

+0

Oups. Sans GC, hein. – bbum

+0

Belles astuces. Merci. J'ai essayé l'outil de fuites. Cependant, mon objectif de base est d'écrire une bibliothèque pour vérifier automatiquement les fuites de code dans la construction DEBUG, pour les tests unitaires. J'ai donc besoin de contrôler les points de test du code - activer les fuites en attrapant pour certains threads, lire stat ... pour automatiser la procédure ... J'ai trouvé mes fuites. Il n'y avait rien de mystérieux - juste raté autorelease à un endroit. Cependant, mes deux questions sont toujours ouvertes. – UncleMiF

Questions connexes