fanisg:Ξεκίνησα σουλούπωμα... δεν θέλω τα αντικείμενα να είναι references, πρέπει να είναι αυτόνομα.
Εξακολουθείς να μπερδεύεις τις έννοιες. Δεν είναι τα αντικείμενα references ούτε υπάρχουν αντικείμενα που δεν είναι αυτόνομα. Αυτόνομα είναι όλα. Όταν όμως χρησιμοποιείς ένα αντικείμενο, ουσιαστικά χρησιμοποιείς ένα reference, ένα pointer στην πραγματική του θέση στη μνήμη. Ακόμα και struct αν είναι το αντικείμενο, μέσω reference του μιλάς. Reference σημαίνει "αναφορά". Αναφέρεσαι σε ένα αντικείμενο, δεν είσαι το αντικείμενο.
Επίσης, η έννοια της "αυτονομίας" δεν υπάρχει. Το ότι ο garbage collector ελέγχει τα references μεταξύ των αντικειμένων δεν σημαίνει ότι υπάρχει κάποια εξάρτηση μεταξύ τους, το αντίθετο. ΕΠΕΙΔΗ ΕΙΝΑΙ ΑΝΕΞΑΡΤΗΤΑ πρέπει να τα ελέγξει. Αν υπήρχε σχέση parent child, ένα child δεν θα μπορούσε ποτέ να κρατήσει ζωντανό ένα parent. Η κορυφή δεν θα μπορούσε ποτέ να κρατήσει ζωντανό ένα τρίγωνο. Αυτό όμως δεν συμβαίνει.
Ακόμα και αν χρησιμοποιήσεις structs τα οποία περνάς στα functions byval (οπότε δημιουργείς αντίγραφα) δεν θα κάνεις τίποτε γιατί το πρόβλημα δεν ήταν το byval αλλά οι σχέσεις μεταξύ των αρχικών αντικειμένων.
Δοκίμασε πρώτα με καθαρά αντικείμενα και χωρίς πειρασμένες παραμέτρους στις μεταβλητές, χωρίς αντικείμενα που "μοιράζονται", για να δεις πως δουλεύει κανονικά το .NET και μετά δοκίμασε τις βελτιώσεις. Η συμπεριφορά που βλέπεις τώρα δεν είναι ομαλή και οφείλεται στις παρεμβάσεις που έχουν γίνει.
Τώρα, για να πεισθούμε ότι όντως δεν χάνεται μνήμη και τα references δεν φταίνε, έφτιαξα τις παρακάτω κλάσεις για τρίγωνα:
public class Point3D
{
public int X { get; set; }
public int Y { get; set; }
public int Z { get; set; }
}
public class Triangle
{
public Point3D[] Vertices { get; set; }
}
Και έφτιαξα και μία λίστα με 800.000 τρίγωνα, τα οποία πειράζω με τον παρακάτω κώδικα:
static void Main(string[] args)
{
Console.WriteLine("Idle");
Console.ReadKey();
var triangles = Generate(800000);
Console.WriteLine("Generated");
Console.ReadKey();
Move(triangles);
Console.WriteLine("Moved");
Console.ReadKey();
}
private static void Move(List<Triangle> triangles)
{
foreach (Triangle triangle in triangles)
{
MoveTriangle(triangle);
}
}
private static void MoveTriangle(Triangle triangle)
{
foreach (Point3D edge in triangle.Vertices)
{
edge.X += 10;
edge.Y += 10;
edge.Z += 10;
}
}
public static List<Triangle> Generate(int count)
{
var triangles=new List<Triangle>();
var r=new Random();
for(int i=0;i<count;i++)
{
triangles.Add(new Triangle {
Vertices=new [] {
new Point3D{X=r.Next(),Y=r.Next(),Z=r.Next()},
new Point3D{X=r.Next(),Y=r.Next(),Z=r.Next()},
new Point3D{X=r.Next(),Y=r.Next(),Z=r.Next()},
}});
}
return triangles;
}
Η μνήμη του προγράμματος ανεβαίνει στα 90 MB περίπου μόλις δημιουργηθούν τα αντικείμενα και παραμένει εκεί, παρότι (ή μάλλον επειδή) η λίστα και τα αντικείμενα περνάνε ως references στη Move και τη MoveTriangle.
Δοκίμασα επίσης να βγάλω τον κώδικα Generate, Move σε function και να το επαναλάβω 100 φορές. Αυτό που είδα ήταν ότι η μνήμη ανεβοκατέβαινε μεταξύ 90 και 160 MB καθώς ο GC δεν έτρεχε αμέσως μετά από κάθε iteration. Αυτό όμως δεν με επηρέαζε καθώς στο τέλος η μνήμη πάντα καθάριζε.
Παναγιώτης Καναβός, Freelancer
Twitter: http://www.twitter.com/pkanavos