Форум программистов, компьютерный форум, киберфорум
Java
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.63/8: Рейтинг темы: голосов - 8, средняя оценка - 4.63
2 / 2 / 0
Регистрация: 18.01.2011
Сообщений: 14

Пологите разобраться со сборщиком мусора! Не удаляются объекты, переполнение памяти..

21.01.2012, 22:15. Показов 1601. Ответов 5
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Добрый день.
Я только приступил к изучению Java, так что скорее всего я просто что-то делаю не так.
Подскажите пожалуйста, что именно..
Ситуация такая, программа генерирует файл объектов, с рандомом заполненными полями.
(для последующей сортировки)
Вот фрагмент кода, который пишет эти объекты в файл:
Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
   File fo = new File("c:/temp.tmp");    
         user temp_user = new user();
         
        ObjectOutputStream outForSort =
                    new ObjectOutputStream (new FileOutputStream(fo));
          System.out.println("Создание файла...");
       
          for (i=1; i<=n; i++) {          
             temp_user = new user();
              temp_user.number = random.nextInt(50000)+1;
              temp_user.name = RandWord(0);
              temp_user.LastName = RandWord(0);
              temp_user.MiddleName = RandWord(0);      
              temp_user.ave = 14 + random.nextInt(20);
            outForSort.writeObject(temp_user);
           temp_user = null;
          }
       System.gc();
        outForSort.close();
При таком подходе программа очень быстро заполняет память, хотя, на сколько я понимаю,
после строки temp_user = new user(); старый объект должен быть удален сборщиком мусора т.к. на него нет ссылок, даже уже явно удалил на него ссылку temp_user = null; , но объекты упорно остаются в памяти до её переполнения. Вызванный System.gc(); после выхода из цикла освобождает метров 100, при использовании программой памяти ~5 Гб.
Изночально хотел сделать так:
Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
   File fo = new File("c:/temp.tmp");             
         
        ObjectOutputStream outForSort =
                    new ObjectOutputStream (new FileOutputStream(fo));
          System.out.println("Создание файла...");
 
       user temp_user = new user();
          for (i=1; i<=n; i++) {          
              temp_user.number = random.nextInt(50000)+1;
              temp_user.name = RandWord(0);
              temp_user.LastName = RandWord(0);
              temp_user.MiddleName = RandWord(0);      
              temp_user.ave = 14 + random.nextInt(20);
            outForSort.writeObject(temp_user);
          }
        outForSort.close();
Но так, почему-то в файл пишется один и тот-же объект! В самом цикле поля меняются, а когда считываешь их из файла, то каждый прочитанный объект полностью равен первому созданному...
Если делать как в первом случае, то все читается правильно, но вот проблема с памятью..
Что я делаю не так?
0
cpp_developer
Эксперт
20123 / 5690 / 1417
Регистрация: 09.04.2010
Сообщений: 22,546
Блог
21.01.2012, 22:15
Ответы с готовыми решениями:

Удаляются ли перекрестные ссылки сборщиком мусора
День добрый! Есть такой теоретический вопрос. Пусть есть классы A, B, и С. Классы A и B хранят ссылки друг на друга. Класс C хранит...

Уничтожается ли объект сборщиком мусора в приложенном коде
Доброго дня Вам. Возник вопрос: имеем код namespace VNServer { public partial class MainForm : Form { .... ...

Сборщик мусора удаляет нужные объекты
Добрый день. Есть класс, в котором запускается мультимедийный таймер из библиотеки winmm.dll. При попытке сборки мусора приложение...

5
 Аватар для mutagen
2587 / 2260 / 257
Регистрация: 14.09.2011
Сообщений: 5,185
Записей в блоге: 18
22.01.2012, 00:03
попробуйте флушить стрим, так как без флуша даже явный вызов гс не помогает
вот мой тест, дольше 500Мб мне было ждать лень, но думаю всё будет ок пока не превысим максимальный размер файла на фс.
Java
1
2
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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Random;
 
public class TryToCatchOutOfMemory {
 
    public static void main(String[] args) throws FileNotFoundException, IOException {
        File fo = new File("temp.tmp");
        User user = null;
        int flusher = 0;
        Random random = new Random();
        ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(fo));
        metka: for (long i = 0; i < Long.MAX_VALUE; i++) {
            user = new User(random.nextInt(), new RandWord(random.nextInt(5)).toString(),
                    new RandWord(random.nextInt(5)).toString(), new RandWord(random.nextInt(5)).toString(), 14 + random.nextInt());
            out.writeObject(user);
            if (flusher++ == 10000) {
                out.flush();
                flusher = 0;
                // System.gc();
                System.out.println("size is " + fo.length());
                if (fo.length() > 5000000) {
                    System.out.println(i + " objects written");
                    break metka;
                }
            }
 
        }
        out.close();
    }
 
    static class RandWord implements Serializable {
        private static final long serialVersionUID = -6466789454006946221L;
        private String[] arr = { "one", "two", "thre", "четыре", "пять" };
        private int rnd;
 
        public RandWord(int i) {
            if (i < 5)
                rnd = i;
            else
                rnd = 0;
        }
 
        @Override
        public String toString() {
            return this.arr[rnd];
        }
 
    }
 
    static class User implements Serializable {
        private static final long serialVersionUID = 4222311633962799202L;
        private int number;
        private String name;
        private String lastName;
        private String middleName;
        private int age;
 
        public int getNumber() {
            return number;
        }
 
        public void setNumber(int number) {
            this.number = number;
        }
 
        public String getName() {
            return name;
        }
 
        public void setName(String name) {
            this.name = name;
        }
 
        public String getLastName() {
            return lastName;
        }
 
        public void setLastName(String lastName) {
            this.lastName = lastName;
        }
 
        public String getMiddleName() {
            return middleName;
        }
 
        public void setMiddleName(String middleName) {
            this.middleName = middleName;
        }
 
        public int getAge() {
            return age;
        }
 
        public void setAge(int age) {
            this.age = age;
        }
 
        public User(int number, String name, String lastName, String middleName, int age) {
            this.number = number;
            this.name = name;
            this.lastName = lastName;
            this.middleName = middleName;
            this.age = age;
        }
 
        @Override
        public int hashCode() {
            final int prime = 31;
            int result = 1;
            result = prime * result + age;
            result = prime * result + ((lastName == null) ? 0 : lastName.hashCode());
            result = prime * result + ((middleName == null) ? 0 : middleName.hashCode());
            result = prime * result + ((name == null) ? 0 : name.hashCode());
            result = prime * result + number;
            return result;
        }
 
        @Override
        public boolean equals(Object obj) {
            if (this == obj)
                return true;
            if (obj == null)
                return false;
            if (getClass() != obj.getClass())
                return false;
            User other = (User) obj;
            if (age != other.age)
                return false;
            if (lastName == null) {
                if (other.lastName != null)
                    return false;
            } else if (!lastName.equals(other.lastName))
                return false;
            if (middleName == null) {
                if (other.middleName != null)
                    return false;
            } else if (!middleName.equals(other.middleName))
                return false;
            if (name == null) {
                if (other.name != null)
                    return false;
            } else if (!name.equals(other.name))
                return false;
            if (number != other.number)
                return false;
            return true;
        }
 
    }
}
1
2 / 2 / 0
Регистрация: 18.01.2011
Сообщений: 14
22.01.2012, 19:03  [ТС]
На сколько я понял мой вариант должен выглядеть так:
Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
File fo = new File("c:/temp.tmp");    
        user temp_user = new user();
         
        ObjectOutputStream outForSort =
                    new ObjectOutputStream (new FileOutputStream(fo));
          System.out.println("Создание файла...");
       
          int flusher = 0;
          
           for (i=1; i<=n; i++) { 
             temp_user = new user();
              temp_user.number = random.nextInt(50000)+1;
              temp_user.name = RandWord(0);
              temp_user.LastName = RandWord(0);
              temp_user.MiddleName = RandWord(0);      
              temp_user.ave = 14 + random.nextInt(20);
            outForSort.writeObject(temp_user);
        
            if (flusher++ == 100000) {
            outForSort.flush();
            flusher = 0;
            System.gc();
             System.out.println("Записанно "+i+" объектов...");
            }
          }
        outForSort.close();
Не помогло..
Запись в файл 10 000 000 объектов отражается на памяти так:


На момент, когда файл весит 500 метров, программа занимает около 4,5 гигов памяти...
Правда в конце работы память вроде как начинает чиститься, но как-то позновато..
0
 Аватар для mutagen
2587 / 2260 / 257
Регистрация: 14.09.2011
Сообщений: 5,185
Записей в блоге: 18
22.01.2012, 22:22
У меня есть подозрение что ваш класс(или метод) RandWord генеря слова использует конкатенацию строк через +, операции со строками просаживают JVM даже на уровне 15К строк. Может быть если Вы выложите код классов участников, то я помогу найти прожорливую часть.

PS: вызывать явно гс это моветон, таким образом вы сбиваете с толку jit и оптимизатор.
0
2 / 2 / 0
Регистрация: 18.01.2011
Сообщений: 14
22.01.2012, 22:39  [ТС]
Code
1
2
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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
package сортировка.файла.пузырьком;
import java.io.*;
import java.util.ArrayList;
import java.util.Random;
import java.util.Scanner;
 
/**
 *
 * @author Александр
 */
class user implements Serializable {
    public long number;
    public String name;
    public String LastName;
    public String MiddleName;
    public long ave;
    
    public void user() {
     number=1; 
     name = "";
     LastName = "";
     MiddleName = "";
     ave = 0;
    } 
}
 
public class СортировкаФайлаПузырьком {
     public static String RandWord (int n ) {
            /* Генереруем случайное слово */
    char tempmas[] = {'a','b','c','d','e','f','g','h','i','j','k','l','m',
                       'n','o','p','q','r','s','t','u','v','w','x','y','x'};
     String s = "";
     Random random = new Random();  
     
     if ((n < 5) | (n > 24)) { n = 5 + random.nextInt(20); }
      
      int i,cod;
      
      for (i = 1; i != n+1; i++)
      {
        cod = random.nextInt(26);
        if (i != 1) { s += tempmas[cod]; } // С большой буквы
               else { s += Character.toUpperCase(tempmas[cod]); }
      } 
    return s;
  }
     
    public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
        Random random = new Random();
        Scanner in = new Scanner(System.in);
        long i; long n = in.nextLong();
        
        File fo = new File("c:/temp.tmp");    
        user temp_user = new user();
         
        ObjectOutputStream outForSort =
                    new ObjectOutputStream (new FileOutputStream(fo));
          System.out.println("Создание файла...");
       
          int flusher = 0;
          
           for (i=1; i<=n; i++) { 
             temp_user = new user();
              temp_user.number = random.nextInt(50000)+1;
              temp_user.name = RandWord(0);
              temp_user.LastName = RandWord(0);
              temp_user.MiddleName = RandWord(0);      
              temp_user.ave = 14 + random.nextInt(20);
            outForSort.writeObject(temp_user);
        
            if (flusher++ == 100000) {
            outForSort.flush();
            flusher = 0;
          //  System.gc();
             System.out.println("Записанно "+i+" объектов...");
            }
          }
        outForSort.close();
        
        double size = fo.length();
        size = size/1024/1024/1024;
        System.out.println("Файл создан: "+fo.getPath()+" (Размер "+size+" Гигабайт, "+i+" объектов)");
        System.out.println("Закачиваем файл в память...");
        
        ObjectInputStream inSort =
                    new ObjectInputStream(new FileInputStream(fo));
        
        ArrayList <user> arrl = new ArrayList();
         for (i=1; i<=n; i++) {  
             arrl.add((user)inSort.readObject());  
         } 
         System.out.println("Файл в памяти! ");
    }
}
0
 Аватар для mutagen
2587 / 2260 / 257
Регистрация: 14.09.2011
Сообщений: 5,185
Записей в блоге: 18
22.01.2012, 23:51
Вот решение - подробности в коментах:
Java
1
2
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
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
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
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Random;
import java.util.Scanner;
 
/**
 * @author Александр, corrections by mutagen
 */
class User implements Serializable {
    private static final long serialVersionUID = 1450745357820872222L;
    private long number;
    private String name;
    private String LastName;
    private String MiddleName;
    private long ave;
 
    // ошибка !!! - вы определили метод а не конструктор и без его вызова ничего
    // не инициализировано
    // public void user() {
    // number=1;
    // name = "";
    // LastName = "";
    // MiddleName = "";
    // ave = 0;
    // }
 
    public User() {
        number = 1;
        name = "";
        LastName = "";
        MiddleName = "";
        ave = 0;
    }
 
    public long getNumber() {
        return number;
    }
 
    public void setNumber(long number) {
        this.number = number;
    }
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
    public String getLastName() {
        return LastName;
    }
 
    public void setLastName(String lastName) {
        LastName = lastName;
    }
 
    public String getMiddleName() {
        return MiddleName;
    }
 
    public void setMiddleName(String middleName) {
        MiddleName = middleName;
    }
 
    public long getAve() {
        return ave;
    }
 
    public void setAve(long ave) {
        this.ave = ave;
    }
 
    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((LastName == null) ? 0 : LastName.hashCode());
        result = prime * result + ((MiddleName == null) ? 0 : MiddleName.hashCode());
        result = prime * result + (int) (ave ^ (ave >>> 32));
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        result = prime * result + (int) (number ^ (number >>> 32));
        return result;
    }
 
    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        User other = (User) obj;
        if (LastName == null) {
            if (other.LastName != null)
                return false;
        } else if (!LastName.equals(other.LastName))
            return false;
        if (MiddleName == null) {
            if (other.MiddleName != null)
                return false;
        } else if (!MiddleName.equals(other.MiddleName))
            return false;
        if (ave != other.ave)
            return false;
        if (name == null) {
            if (other.name != null)
                return false;
        } else if (!name.equals(other.name))
            return false;
        if (number != other.number)
            return false;
        return true;
    }
 
    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();
        builder.append("User [number=");
        builder.append(number);
        builder.append(", name=");
        builder.append(name);
        builder.append(", LastName=");
        builder.append(LastName);
        builder.append(", MiddleName=");
        builder.append(MiddleName);
        builder.append(", ave=");
        builder.append(ave);
        builder.append("]");
        return builder.toString();
    }
 
}
 
public class SortPusirek {
    // в будущем страйтесь придерживаться конвенции имён(метод и проперти с
    // маленькой буквы, а Класс с большой) - иначе вы и себя и других вводите в
    // заблуждение
    public static String randWord(int n, Random random, StringBuilder s) {
        /* Генереруем случайное слово */
        char tempmas[] = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',
                't', 'u', 'v', 'w', 'x', 'y', 'x' };
        // каждый вызов метода создаёт новый рандом - плохо, выносим за метод
        // Random random = new Random();
 
        if ((n < 5) | (n > 24)) {
            n = 5 + random.nextInt(20);
        }
        // сброс буфера
        s.setLength(0);
        int i, cod;
 
        for (i = 1; i != n + 1; i++) {
            cod = random.nextInt(26);
            if (i != 1) {
                // += это очень прожорливая операция, c immutable стрингами - её
                // следует избегать, так как на
                // каждый += чар создаётся новый объект стринг
                // s += tempmas[cod];
                s.append(tempmas[cod]);
            } // С большой буквы
            else {
                // s += Character.toUpperCase(tempmas[cod]);
                s.append(Character.toUpperCase(tempmas[cod]));
            }
        }
        return s.toString();
    }
 
    public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
        Random random = new Random();
        Scanner in = new Scanner(System.in);
        long i;
        long n = in.nextLong();
 
        File fo = new File("temp.tmp");
        User temp_user = null;
 
        ObjectOutputStream outForSort = new ObjectOutputStream(new FileOutputStream(fo));
        System.out.println("Создание файла...");
 
        int flusher = 0;
 
        StringBuilder strBuild = new StringBuilder();
        for (i = 1; i <= n; i++) {
            temp_user = new User();
            temp_user.setNumber(random.nextInt(50000) + 1);
            temp_user.setName(randWord(0, random, strBuild));
            temp_user.setLastName(randWord(0, random, strBuild));
            temp_user.setMiddleName(randWord(0, random, strBuild));
            temp_user.setAve(14 + random.nextInt(20));
            // чтобы убить ссылки наверняка сохраняем не текущий объект а его
            // копию, но тем не менее напарываемся на баг утечки
            // [url]http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6525563[/url]
            // outForSort.writeObject(temp_user);
            outForSort.writeUnshared(temp_user);
            temp_user = null;
            if (flusher++ == 10000) {
                outForSort.flush();
                // используем воркароунд
                outForSort.reset();
                flusher = 0;
                // System.gc();
                // System.out.println("Записанно " + i + " объектов...");
            }
            if (i == 500000)
                System.out.println("Записанно " + i + " объектов...");
        }
        outForSort.close();
 
        double size = fo.length();
        size = size / 1024 / 1024 / 1024;
        System.out.println("Файл создан: " + fo.getPath() + " (Размер " + size + " Гигабайт, " + i + " объектов)");
        /**
         * Совершенно не представляю какой размер памяти вам понадобится для листа
         * после десериализации
         */
 
        // System.out.println("Закачиваем файл в память...");
        //
        // ObjectInputStream inSort = new ObjectInputStream(new
        // FileInputStream(fo));
        //
        // ArrayList<User> arrl = new ArrayList<User>();
        // for (i = 1; i <= n; i++) {
        // arrl.add((User) inSort.readObject());
        // }
        // System.out.println("Файл в памяти! ");
    }
}
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
raxper
Эксперт
30234 / 6612 / 1498
Регистрация: 28.12.2010
Сообщений: 21,154
Блог
22.01.2012, 23:51
Помогаю со студенческими работами здесь

Не удаляются объекты
Скачал на рутрекере Norton Utilities Portable. Запустил. Он сразу мне прилепил папку с невообразимым количеством подпапок по цепочке. ...

После окончания работы со структурой, она будет почищена сборщиком. Что произойдет со структурой в неуправляемой памяти
Доброго времени суток. Прочитал закрепленный топик &quot;Класс Marshal, использование PInvoke, небезопасный код (unsafe)&quot;. Возник...

Где удаляются Implicit Sharing объекты?
Если я сигналом пошлю QByteArray из одного потока. signals: void sendSig(QByteArray sig); emit sendSig(signal); Который...

Не удаляются динамически созданные объекты TImage
Button2Click создает массив объектов Block типа ТImage.. Button3Click должен их удалять..использую метод Free но они не удаляются...

Какие объекты доступны для сборщика мусора на момент вызова System.gc() и почему?
public class GCTest { static class A { private String myName; public A(String myName) { this.myName = myName;


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
6
Ответ Создать тему
Новые блоги и статьи
Программный контроль заполнения реквизита табличной части документа
Maks 02.04.2026
Алгоритм из решения ниже реализован на примере нетипового документа "СписаниеМатериалов", разработанного в конфигурации КА2. Задача: реализовать контроль заполнения реквизита "ПричинаСписания". . .
wmic не является внутренней или внешней командой
Maks 02.04.2026
Решение: DISM / Online / Add-Capability / CapabilityName:WMIC~~~~ Отсюда: https:/ / winitpro. ru/ index. php/ 2025/ 02/ 14/ komanda-wmic-ne-naydena/
Программная установка даты и запрет ее изменения
Maks 02.04.2026
Алгоритм из решения ниже реализован на примере нетипового документа "СписаниеМатериалов", разработанного в конфигурации КА2. Задача: при создании документов установить период списания автоматически. . .
Вывод данных в справочнике через динамический список
Maks 01.04.2026
Реализация из решения ниже выполнена на примере нетипового справочника "Спецтехника" разработанного в конфигурации КА2. Задача: вывести данные из ТЧ нетипового документа. . .
Программное заполнения текстового поля в реквизите формы документа
Maks 01.04.2026
Алгоритм из решения ниже реализован на нетиповом документе "ВыдачаОборудованияНаСпецтехнику" разработанного в конфигурации КА2, в дополнении к предыдущему решению. На форме документа создается. . .
К слову об оптимизации
kumehtar 01.04.2026
Вспоминаю начало 2000-х, университет, когда я писал на Delphi. Тогда среди программистов на форумах активно обсуждали аккуратную работу с памятью: нужно было следить за переменными, вовремя. . .
Идея фильтра интернета (сервер = слой+фильтр).
Hrethgir 31.03.2026
Суть идеи заключается в том, чтобы запустить свой сервер, о чём я если честно мечтал давно и давно приобрёл книгу как это сделать. Но не было причин его запускать. Очумелые учёные напечатали на. . .
Модель здравосоХранения 6. ESG-повестка и устойчивое развитие; углублённый анализ кадрового бренда
anaschu 31.03.2026
В прикрепленном документе раздумья о том, как можно поменять модель в будущем
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru